mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 12:21:31 +00:00
This commit is contained in:
@@ -326,7 +326,7 @@ jobs:
|
||||
# RAM headroom on ubuntu-latest (16 GB ceiling) — operator-confirmed
|
||||
# in Phase 0 / frozen decision 0.14 prototype-branch run. If RAM
|
||||
# regresses, fall back to bucketed matrix per
|
||||
# cowork/ci-pipeline-cleanup/decisions-revised.md.
|
||||
# the project's frozen-decisions log.
|
||||
#
|
||||
# The Windows matrix (deploy-vendor-e2e-windows) was deleted entirely
|
||||
# per Phase 6 / frozen decision 0.5 (revises Bundle II decision 0.4).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Load-test workflow — closes the #8 acquisition-readiness blocker from
|
||||
# the 2026-05-01 issuer coverage audit (see
|
||||
# cowork/issuer-coverage-audit-2026-05-01/RESULTS.md).
|
||||
# the 2026-05-01 issuer coverage audit).
|
||||
#
|
||||
# CADENCE: workflow_dispatch + weekly cron, NOT per-push. Load tests
|
||||
# are minutes long and don't provide useful per-PR signal — per-push
|
||||
|
||||
@@ -20,7 +20,7 @@ name: security-deep-scan
|
||||
#
|
||||
# Each step is best-effort — failures are uploaded as artefacts but do
|
||||
# NOT block the workflow. Triage happens via the Bundle-7 receipt
|
||||
# directory under cowork/comprehensive-audit-2026-04-25/tool-output/.
|
||||
# the project's comprehensive-audit tool-output directory.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
# package is mutated independently; the per-package summary line
|
||||
# (`The mutation score is X.YZ`) is grep-extracted into the receipt.
|
||||
# Acceptance threshold: ≥80% kill ratio per package; surviving
|
||||
# mutants get triaged in cowork/comprehensive-audit-2026-04-25/
|
||||
# mutants get triaged in the project's comprehensive-audit notes/
|
||||
# d003-mutation-results.md (per-mutant action item or
|
||||
# equivalent-mutation justification).
|
||||
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ type AgentConfig struct {
|
||||
// ErrAgentRetired is the sentinel returned by [Agent.Run] when the control
|
||||
// plane responds with HTTP 410 Gone to a heartbeat or work-poll request — the
|
||||
// canonical signal that this agent's row has been soft-retired server-side
|
||||
// (see I-004 in cowork/certctl-coverage-gap-audit.md). The binary must
|
||||
// (see I-004 in the project's coverage-gap audit). The binary must
|
||||
// terminate cleanly: an init-system restart would only produce another 410
|
||||
// and wedge the host in a restart loop. main() translates this sentinel into
|
||||
// a zero exit code so systemd (Restart=on-failure) and launchd do not respawn
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ func main() {
|
||||
notificationService.SetOwnerRepo(ownerRepo)
|
||||
|
||||
// Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Per-policy
|
||||
// (per the project's deep-research deliverable, Part 5). Per-policy
|
||||
// multi-channel expiry-alert metrics. Same instance is wired into
|
||||
// the notification service (recording side, every
|
||||
// SendThresholdAlertOnChannel call reports its outcome) AND into
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
# CI Pipeline Cleanup — Phase 0 Baseline
|
||||
|
||||
> Captured against repo HEAD `1de61e91cf07449356d9046a76499c86efe413b1` (operator tag `v2.0.66`) on 2026-04-30.
|
||||
> Each subsequent Phase that changes a number references this baseline.
|
||||
|
||||
## Repo state
|
||||
|
||||
**HEAD SHA:** `1de61e91cf07449356d9046a76499c86efe413b1`
|
||||
|
||||
**Operator-stamped tag:** `v2.0.66`
|
||||
|
||||
## ci.yml shape
|
||||
|
||||
- Total lines: `1488`
|
||||
- Total named steps: `53`
|
||||
- Named regression-guard steps: 22 (enumerated below)
|
||||
|
||||
### The 22 regression-guard steps
|
||||
|
||||
```
|
||||
81: - name: Forbidden auth-type literal regression guard (G-1)
|
||||
144: - name: Forbidden bare InsecureSkipVerify regression guard (L-001)
|
||||
180: - name: Forbidden bare FROM regression guard (H-001)
|
||||
201: - name: Forbidden missing USER regression guard (M-012)
|
||||
228: - name: Forbidden README JWT advertising regression guard (H-009)
|
||||
254: - name: Forbidden api_key_hash JSON-shape regression guard (G-2)
|
||||
311: - name: Forbidden plaintext HEALTHCHECK regression guard (U-2)
|
||||
360: - name: Forbidden migration mount in compose initdb (U-3)
|
||||
417: - name: Forbidden StatusBadge dead-key + TS phantom-field regression guard (D-1 + D-2)
|
||||
569: - name: Forbidden client-side bulk-action loop regression guard (L-1)
|
||||
613: - name: Forbidden orphan-CRUD client function regression guard (B-1)
|
||||
665: - name: Forbidden strings.Contains(err.Error()) regression guard (S-2)
|
||||
868: - name: QA-doc Part-count drift guard
|
||||
886: - name: QA-doc seed-count drift guard
|
||||
938: - name: Test-naming convention guard (hard-fail)
|
||||
982: - name: Forbidden hardcoded source-count prose regression guard (S-1)
|
||||
1027: - name: Documented orphan client fns sync guard (P-1)
|
||||
1063: - name: Frontend page-coverage regression guard (T-1)
|
||||
1118: - name: Bundle-8 / L-015 target=_blank rel=noopener regression guard
|
||||
1147: - name: Bundle-8 / L-019 dangerouslySetInnerHTML regression guard
|
||||
1176: - name: Bundle-8 / M-009 + M-029 Pass 1 mutation contract guard (hard zero)
|
||||
1220: - name: Forbidden env-var docs drift regression guard (G-3)
|
||||
```
|
||||
|
||||
## SA1019 site count
|
||||
|
||||
- **Operator-on-workstation deliverable** — sandbox cannot run `staticcheck`.
|
||||
- ci.yml inline comment claims "6 sites" (`middleware.NewAuth × 3`, `csr.Attributes`, `elliptic.Marshal`).
|
||||
- Source-grep at HEAD shows:
|
||||
- `internal/api/handler/scep.go`: `csr.Attributes` references present
|
||||
- `internal/connector/issuer/local/local.go`: `elliptic.Marshal` historic refs (already migrated per bundle9_coverage_test.go byte-equivalence test)
|
||||
- `cmd/server/main_test.go`: `middleware.NewAuth` references TBD
|
||||
- Operator must run `staticcheck ./... 2>&1 | grep SA1019` on workstation and update Phase 3 plan with the actual site list.
|
||||
|
||||
## Dockerfile inventory (verified 4)
|
||||
|
||||
```
|
||||
./Dockerfile.agent
|
||||
./Dockerfile
|
||||
./deploy/test/f5-mock-icontrol/Dockerfile
|
||||
./deploy/test/libest/Dockerfile
|
||||
```
|
||||
|
||||
## Migration up/down balance
|
||||
|
||||
- ups: `24`
|
||||
- downs: `24`
|
||||
- missing downs: `0`
|
||||
|
||||
## OpenAPI ↔ handler parity gap (verified)
|
||||
|
||||
- operationIds in api/openapi.yaml: `136`
|
||||
- r.Register calls in router.go: `149`
|
||||
- Gap to root-cause in Phase 9: 13 routes
|
||||
|
||||
## docker-compose.test.yml sidecars
|
||||
|
||||
```
|
||||
52: certctl-tls-init:
|
||||
107: postgres:
|
||||
135: pebble-challtestsrv:
|
||||
150: pebble:
|
||||
178: step-ca:
|
||||
213: certctl-server:
|
||||
363: nginx:
|
||||
391: certctl-agent:
|
||||
449: libest-client:
|
||||
488: apache-test:
|
||||
502: haproxy-test:
|
||||
515: traefik-test:
|
||||
533: caddy-test:
|
||||
548: envoy-test:
|
||||
562: postfix-test:
|
||||
577: dovecot-test:
|
||||
591: openssh-test:
|
||||
613: f5-mock-icontrol:
|
||||
631: k8s-kind-test:
|
||||
648: windows-iis-test:
|
||||
666: certctl-test:
|
||||
```
|
||||
|
||||
## Makefile::verify body (existing)
|
||||
|
||||
```
|
||||
verify:
|
||||
@echo "==> fmt"
|
||||
@go fmt ./... | { ! grep -q '.'; } || (echo "gofmt produced changes — commit them" && exit 1)
|
||||
@echo "==> go vet ./..."
|
||||
@go vet ./...
|
||||
@echo "==> golangci-lint run ./... (incl. staticcheck ST*)"
|
||||
@which golangci-lint > /dev/null || (echo "Installing golangci-lint..." && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest)
|
||||
@golangci-lint run ./... --timeout 5m
|
||||
@echo "==> go test -short ./..."
|
||||
@go test -short -count=1 ./...
|
||||
@echo ""
|
||||
@echo "verify: PASS — safe to commit"
|
||||
|
||||
```
|
||||
|
||||
## RAM headroom for collapsed vendor-e2e job
|
||||
|
||||
- **Operator-on-workstation deliverable** — requires a prototype branch with the collapsed job + `docker stats` polling.
|
||||
- Per Phase 0 frozen decision 0.14: if peak RSS ≤ 12 GB on ubuntu-latest (16 GB ceiling), single-job collapse is approved.
|
||||
- If > 12 GB, fall back to bucketed-matrix design documented in `cowork/ci-pipeline-cleanup/decisions-revised.md`.
|
||||
|
||||
## Coverage thresholds at HEAD
|
||||
|
||||
```
|
||||
778: if [ "$(echo "$SERVICE_COV < 70" | bc -l)" -eq 1 ]; then
|
||||
779: echo "::error::Service layer coverage ${SERVICE_COV}% is below 70% (Bundle R-CI-extended floor — add tests, do not lower the gate)"
|
||||
782: if [ "$(echo "$HANDLER_COV < 75" | bc -l)" -eq 1 ]; then
|
||||
783: echo "::error::Handler layer coverage ${HANDLER_COV}% is below 75% (Bundle R-CI-extended floor — add tests, do not lower the gate)"
|
||||
786: if [ "$(echo "$DOMAIN_COV < 40" | bc -l)" -eq 1 ]; then
|
||||
787: echo "::error::Domain layer coverage ${DOMAIN_COV}% is below 40% threshold"
|
||||
790: if [ "$(echo "$MIDDLEWARE_COV < 30" | bc -l)" -eq 1 ]; then
|
||||
791: echo "::error::Middleware layer coverage ${MIDDLEWARE_COV}% is below 30% threshold"
|
||||
802: if [ "$(echo "$CRYPTO_COV < 88" | bc -l)" -eq 1 ]; then
|
||||
803: echo "::error::Crypto package coverage ${CRYPTO_COV}% is below 88% (Bundle R closure floor — add tests, do not lower the gate)"
|
||||
832: if [ "$(echo "$LOCAL_ISSUER_COV < 86" | bc -l)" -eq 1 ]; then
|
||||
833: echo "::error::Local-issuer coverage ${LOCAL_ISSUER_COV}% is below 86% (Bundle R closure floor — add tests, do not lower the gate)"
|
||||
842: if [ "$(echo "$ACME_COV < 80" | bc -l)" -eq 1 ]; then
|
||||
843: echo "::error::ACME issuer coverage ${ACME_COV}% is below 80% (Bundle R-CI-extended floor — add tests, do not lower the gate)"
|
||||
846: if [ "$(echo "$STEPCA_COV < 80" | bc -l)" -eq 1 ]; then
|
||||
847: echo "::error::StepCA issuer coverage ${STEPCA_COV}% is below 80% (Bundle L.B closure floor — add tests, do not lower the gate)"
|
||||
850: if [ "$(echo "$MCP_COV < 85" | bc -l)" -eq 1 ]; then
|
||||
851: echo "::error::MCP coverage ${MCP_COV}% is below 85% (Bundle K closure floor — add tests, do not lower the gate)"
|
||||
```
|
||||
|
||||
## CodeQL workflow (no changes)
|
||||
|
||||
- File: `.github/workflows/codeql.yml` (`81` lines)
|
||||
- Matrix: `[go, javascript-typescript]` — 2 status checks per push
|
||||
- Trigger: push to master, PR to master, weekly Sunday cron
|
||||
|
||||
## Status check accounting (verified)
|
||||
|
||||
Today: 1 `go-build-and-test` + 1 `frontend-build` + 1 `helm-lint` + 12 `deploy-vendor-e2e (<vendor>)` + 2 `deploy-vendor-e2e-windows (<vendor>)` + 2 `CodeQL Analyze (<lang>)` = **19 status checks per push**.
|
||||
|
||||
After cleanup: 1 `go-build-and-test` + 1 `frontend-build` + 1 `helm-lint` + 1 `deploy-vendor-e2e` + 1 `image-and-supply-chain` + 2 `CodeQL Analyze (<lang>)` = **7 status checks per push**.
|
||||
@@ -1,53 +0,0 @@
|
||||
# CI Pipeline Cleanup — Deliberate Revisions of Bundle II Decisions
|
||||
|
||||
This bundle deliberately revises two Bundle II frozen decisions. Both revisions are recorded here for audit trail and acknowledged in the per-Phase commits that implement them.
|
||||
|
||||
## Bundle II decision 0.4 → revised by ci-pipeline-cleanup decision 0.5
|
||||
|
||||
**Bundle II 0.4 (original):** "IIS e2e strategy — `mcr.microsoft.com/windows/servercore:ltsc2022` Windows containers via Docker Desktop on Windows hosts. Linux CI runners CAN'T run Windows containers, so the IIS e2e suite runs on a separate Windows-runner CI matrix job (or operator's local Windows host for development). Documented limitation."
|
||||
|
||||
**ci-pipeline-cleanup 0.5 (revision):** Delete the Windows-runner CI matrix entirely.
|
||||
|
||||
**Rationale for revision:**
|
||||
|
||||
1. The matrix can't physically work on `windows-latest` GitHub-hosted runners today. Verified via the failure logs from CI run `25183374742` (commit `1de61e9`):
|
||||
- `wincertstore` job: `error during connect: ... open //./pipe/docker_engine: The system cannot find the file specified` — Docker daemon not started in Windows-containers mode.
|
||||
- `iis` job: image pulled successfully (so the new digest is correct), then died at `failed to create network deploy_certctl-test: could not find plugin bridge in v1 plugin registry: plugin not found` — `bridge` network driver doesn't exist on Windows Docker (uses `nat`).
|
||||
|
||||
2. Even if both Docker-daemon and network-driver issues were fixed, the matrix would validate nothing of substance. Verified by source-grep: all 16 functions matching `TestVendorEdge_(IIS|WinCertStore)_*` in `deploy/test/vendor_e2e_phase3_to_13_test.go` are `t.Log` placeholders that exercise no IIS-specific behavior. The real IIS connector validation lives in `internal/connector/target/iis/` unit tests (run on Linux in `go-build-and-test` — already green per push).
|
||||
|
||||
3. Bundle II decision 0.14 explicitly required operator manual smoke against a real instance for "verified" status in the vendor matrix. Moving IIS + WinCertStore validation to a documented operator playbook in `docs/connector-iis.md` satisfies that criterion better than a fake CI matrix that passes by skipping.
|
||||
|
||||
**Preservation:** the `windows-iis-test` sidecar stays in `deploy/docker-compose.test.yml` under `profiles: [deploy-e2e-windows]` — operators on a Windows host can opt in via `docker compose --profile deploy-e2e-windows up -d windows-iis-test`. Linux CI never activates this profile.
|
||||
|
||||
## Bundle II decision 0.9 → revised by ci-pipeline-cleanup decision 0.4
|
||||
|
||||
**Bundle II 0.9 (original):** "CI parallelism — Each vendor e2e gets its own GitHub Actions matrix job. Vendor failures surface independently in the CI status check (operator sees 'K8s 1.31 vendor-edge fail' as a discrete check, not a generic 'integration tests failed')."
|
||||
|
||||
**ci-pipeline-cleanup 0.4 (revision):** Single `deploy-vendor-e2e` job replaces the 12-job matrix; per-vendor visibility partially restored via skip-detection guard messages.
|
||||
|
||||
**Rationale for revision:**
|
||||
|
||||
1. The per-vendor granularity Bundle II decision 0.9 was designed to provide is fake signal. Verified by source-analysis at HEAD:
|
||||
```
|
||||
$ grep -cE 't\.Log\(' deploy/test/{vendor_e2e_phase3_to_13,nginx_vendor_e2e}_test.go
|
||||
deploy/test/nginx_vendor_e2e_test.go:9
|
||||
deploy/test/vendor_e2e_phase3_to_13_test.go:106
|
||||
|
||||
$ awk '/^func TestVendorEdge_/{in_test=1; name=$2; has_assert=0; next}
|
||||
in_test && /^}$/ {if (has_assert) print name; in_test=0}
|
||||
in_test && /t\.(Fatal|Error|Errorf|Fatalf|Fail|Failf)/ {has_assert=1}' \
|
||||
deploy/test/vendor_e2e_phase3_to_13_test.go deploy/test/nginx_vendor_e2e_test.go
|
||||
TestVendorEdge_NGINX_HighConcurrencyDeployUnderLoad_E2E
|
||||
```
|
||||
115 of 116 vendor-edge test functions are `t.Log`-only — they spin up a sidecar, log a one-line description of the vendor quirk, and return. Only 1 has a real assertion.
|
||||
|
||||
2. Per-vendor status-check granularity costs ~9 sec setup overhead × 12 jobs = ~108 sec of pure runner waste per push (verified from CI run `25183374742` job timings).
|
||||
|
||||
3. The single-job version partially restores per-vendor visibility via the skip-detection guard (decision 0.6): if a sidecar fails to start, the affected tests' SKIP names print in the CI output and the build fails. Operators see "TestVendorEdge_K8s_KubeletSyncWaitContract_DefaultTimeout60s_E2E SKIPPED: vendor sidecar 'k8s-kind' not reachable" — same per-vendor signal, just no longer rendered as a separate status-check row.
|
||||
|
||||
**Preservation:** the per-test discoverability via `go test -run 'VendorEdge_<vendor>'` (Bundle II frozen decision 0.6) is unchanged. Only the matrix-jobs-per-vendor part of decision 0.9 is revised; the per-test naming convention stays.
|
||||
|
||||
## Forward-looking note
|
||||
|
||||
Both revisions are limited in scope to CI execution shape — they do NOT delete the test files, the sidecar definitions, or the documentation that Bundle II shipped. Future work could re-introduce per-vendor matrix jobs if test bodies are filled in with real assertions (transforming the t.Log placeholders into actual contract pins). At that point, decision 0.4 + 0.9 should be re-evaluated.
|
||||
@@ -1,64 +0,0 @@
|
||||
# CI Pipeline Cleanup — Frozen Decisions
|
||||
|
||||
> 14 frozen decisions confirmed at Phase 0. Each subsequent Phase references the decision number it implements.
|
||||
|
||||
## 0.1 — Trigger model
|
||||
|
||||
Three-tier split, no mixing:
|
||||
- **On push/PR to master:** blocking, fast, every check earns its keep, target <10 min wall-clock.
|
||||
- **Daily cron + workflow_dispatch:** `security-deep-scan.yml` as-is; slow scans, best-effort, never blocks.
|
||||
- **On tag push (`v*`):** `release.yml` as-is; cross-platform binaries, ghcr.io push, SLSA provenance.
|
||||
|
||||
## 0.2 — Extracted-script location
|
||||
|
||||
`scripts/ci-guards/` at repo root. Operator runs `bash scripts/ci-guards/<id>.sh` locally. Contract documented in `scripts/ci-guards/README.md`.
|
||||
|
||||
## 0.3 — Coverage threshold YAML format
|
||||
|
||||
`.github/coverage-thresholds.yml`. Top-level keys are package paths; each entry has `floor:` (integer pct) + `why:` (multi-line string for load-bearing context). Bash step uses Python (already on the runner) to read the YAML — no `yq` dependency.
|
||||
|
||||
## 0.4 — Vendor matrix collapse policy (REVISES Bundle II decision 0.9)
|
||||
|
||||
Single `deploy-vendor-e2e` job replaces 12-job matrix. Bundle II decision 0.9 said "Each vendor e2e gets its own GitHub Actions matrix job" — this revision recognizes that 115/116 vendor-edge tests are `t.Log` placeholders, so per-vendor status-check granularity is fake signal. Skip-detection guard partially restores per-vendor visibility (SKIP messages name the vendor). Documented as deliberate revision in `cowork/ci-pipeline-cleanup/decisions-revised.md`.
|
||||
|
||||
## 0.5 — Windows IIS validation deletion (REVISES Bundle II decision 0.4)
|
||||
|
||||
Delete `deploy-vendor-e2e-windows` matrix entirely. Bundle II decision 0.4 said "the IIS e2e suite runs on a separate Windows-runner CI matrix job" — this revision recognizes that (a) the matrix can't physically work on `windows-latest` (Docker not started in Windows-containers mode; `bridge` driver missing on Windows Docker), and (b) all 16 IIS + WinCertStore tests are `t.Log` placeholders. Move validation to `docs/connector-iis.md::Operator validation playbook` per Bundle II decision 0.14's third criterion. The `windows-iis-test` sidecar stays in `deploy/docker-compose.test.yml` for operator local use.
|
||||
|
||||
## 0.6 — Skip-detection guard semantics + EXPECTED_SKIPS allowlist
|
||||
|
||||
After `go test -tags integration -run 'VendorEdge_'`, count `^--- SKIP:` lines. Allowlist: 6 JavaKeystore tests in `vendor_e2e_phase3_to_13_test.go` that legitimately t.Log without sidecar. Allowlist file at `scripts/ci-guards/vendor-e2e-skip-allowlist.txt`, one test name per line.
|
||||
|
||||
## 0.7 — SA1019 closure approach
|
||||
|
||||
Close each site individually with byte-equivalence tests where the deprecated API was load-bearing. Then flip `continue-on-error: true` → `false` in the SAME commit. Do NOT split — shipping the gate without closing sites would fail CI on master. Live verification: `staticcheck ./... 2>&1 | grep -c SA1019` returns 0 BEFORE flipping the gate.
|
||||
|
||||
## 0.8 — Image-and-supply-chain placement
|
||||
|
||||
Separate top-level job (not steps in `go-build-and-test`). Two reasons: (a) digest-validity needs network egress to multiple registries (Docker Hub, ghcr.io, mcr.microsoft.com), bundling into go-build blocks Go tests on registry latency. (b) `docker build` is parallel to Go tests; isolating lets it run concurrently.
|
||||
|
||||
## 0.9 — Coverage PR-comment provider
|
||||
|
||||
Default: lightweight self-hosted action that posts a per-PR comment via `gh pr comment`. Avoids paid SaaS. Operator can swap to Codecov/Coveralls later.
|
||||
|
||||
## 0.10 — Docker build smoke scope
|
||||
|
||||
Build all 4 Dockerfiles in the repo: `Dockerfile`, `Dockerfile.agent`, `deploy/test/f5-mock-icontrol/Dockerfile`, `deploy/test/libest/Dockerfile`. The test-sidecar Dockerfiles are load-bearing for vendor-e2e — a syntax error there silently breaks the e2e suite. Tagged `:smoke` and discarded.
|
||||
|
||||
## 0.11 — OpenAPI ↔ handler parity exception YAML
|
||||
|
||||
NEW `api/openapi-handler-exceptions.yaml`. Schema: `documented_exceptions:` list of `{route, why}` entries. The 13-route gap at HEAD is root-caused in Phase 9; most are likely health probes / metrics / SCEP-EST-OCSP wire endpoints that legitimately have no operationId.
|
||||
|
||||
## 0.12 — Branch-protection-rule update timing
|
||||
|
||||
Operator updates GitHub branch-protection rules in Phase 13 AFTER the new pipeline ships and runs green on a feature branch + on the first push to master. Required-checks list changes from 19 → 7 entries. Operator action only — agent cannot do this.
|
||||
|
||||
## 0.13 — Make-target naming for new operator-side scripts
|
||||
|
||||
- `make verify` (existing) — required pre-commit; gofmt + vet + lint + tests
|
||||
- `make verify-deploy` (new) — optional pre-push; digest-validity + OpenAPI parity + docker build smoke (server + agent only — fast subset for local)
|
||||
- `make verify-docs` (new) — required pre-tag; QA-doc Part-count + seed-count drift
|
||||
|
||||
## 0.14 — RAM headroom verification methodology
|
||||
|
||||
Phase 0 deliverable. Operator creates `prototype/ci-pipeline-cleanup-vendor-collapse` branch, runs the collapsed `deploy-vendor-e2e` job once, captures peak RSS via `docker stats --no-stream` snapshots every 30 sec, records max in this baseline doc. If max > 12 GB (75% of 16 GB ceiling), fall back to bucketed matrix (3 jobs × ~4 sidecars). If max ≤ 12 GB, single-job collapse is approved.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Phase 13 Verification Log
|
||||
|
||||
> Captured against repo HEAD post-Phase-12 commit `453ba78` on 2026-04-30.
|
||||
|
||||
## All 22 ci-guards run on HEAD
|
||||
|
||||
```
|
||||
PASS B-1-orphan-crud.sh
|
||||
PASS D-1-D-2-statusbadge-phantom.sh
|
||||
PASS G-1-jwt-auth-literal.sh
|
||||
PASS G-2-api-key-hash-json.sh
|
||||
PASS G-3-env-docs-drift.sh
|
||||
PASS H-001-bare-from.sh
|
||||
PASS H-009-readme-jwt.sh
|
||||
PASS L-001-insecure-skip-verify.sh
|
||||
PASS L-1-bulk-action-loop.sh
|
||||
PASS M-012-no-root-user.sh
|
||||
PASS P-1-documented-orphan-fns.sh
|
||||
PASS S-1-hardcoded-source-counts.sh
|
||||
PASS S-2-strings-contains-err.sh
|
||||
PASS T-1-frontend-page-coverage.sh
|
||||
PASS U-2-plaintext-healthcheck.sh
|
||||
PASS U-3-migration-mount.sh
|
||||
PASS bundle-8-L-015-target-blank-rel-noopener.sh
|
||||
PASS bundle-8-L-019-dangerously-set-inner-html.sh
|
||||
PASS bundle-8-M-009-bare-usemutation.sh
|
||||
PASS digest-validity.sh
|
||||
PASS openapi-handler-parity.sh
|
||||
PASS test-naming-convention.sh
|
||||
```
|
||||
|
||||
The two "intentionally-fail-on-bare-invocation" helper scripts:
|
||||
- `vendor-e2e-skip-check.sh` — needs `test-output.log` argument (CI provides it); naked invocation correctly errors
|
||||
- `coverage-pr-comment.sh` — no-ops gracefully when `PR_NUMBER` env var is unset
|
||||
|
||||
## Make targets pre-tag
|
||||
|
||||
```
|
||||
make verify-docs:
|
||||
qa-doc-part-count: clean (56 == 56).
|
||||
qa-doc-seed-count: clean.
|
||||
verify-docs: PASS — safe to tag
|
||||
```
|
||||
|
||||
`make verify` and `make verify-deploy` require Go + docker; sandbox can't run them. Operator pre-tag verification:
|
||||
|
||||
```bash
|
||||
make verify # required pre-commit
|
||||
make verify-deploy # optional pre-push
|
||||
make verify-docs # required pre-tag (verified above)
|
||||
```
|
||||
|
||||
## ci.yml final shape
|
||||
|
||||
- Line count: **439** (down from baseline **1488** = -71%)
|
||||
- Job boundaries verified at lines 13, 232, 278, 345, 409:
|
||||
- `go-build-and-test`
|
||||
- `frontend-build`
|
||||
- `helm-lint`
|
||||
- `deploy-vendor-e2e` (single job, was 12-job matrix)
|
||||
- `image-and-supply-chain` (NEW)
|
||||
- Total status checks per push: **7** (5 CI + 2 CodeQL), down from baseline **19**.
|
||||
|
||||
## Phase commits (master ahead of v2.0.66)
|
||||
|
||||
```
|
||||
453ba78 ci-pipeline-cleanup Phase 12: docs/ci-pipeline.md + bundle artefacts
|
||||
ce987cc ci-pipeline-cleanup Phase 11: make verify-docs + verify-deploy targets
|
||||
3a69600 ci-pipeline-cleanup Phase 10: coverage PR-comment action
|
||||
19a5e43 ci-pipeline-cleanup Phases 7-9: image-and-supply-chain job
|
||||
d0bc53b ci-pipeline-cleanup Phase 6 follow-up: IIS operator playbook + matrix doc
|
||||
6f6de63 ci-pipeline-cleanup Phase 5+6: collapse vendor matrix; delete Windows matrix
|
||||
71b2245 ci-pipeline-cleanup Phase 4: gofmt parity + go mod tidy drift
|
||||
af72630 ci-pipeline-cleanup Phase 3: staticcheck hard-fail (SA1019 sites verified closed)
|
||||
60f368e ci-pipeline-cleanup Phase 2: coverage thresholds → YAML manifest
|
||||
5b7a022 ci-pipeline-cleanup Phase 1: extract 20 regression guards to scripts/ci-guards/
|
||||
d57910c ci-pipeline-cleanup Phase 0: baseline + frozen decisions + Bundle II revisions
|
||||
```
|
||||
|
||||
## Operator action items post-merge
|
||||
|
||||
1. **GitHub branch protection rule update** — required-checks list changes 19 → 7:
|
||||
```
|
||||
Go Build & Test
|
||||
Frontend Build
|
||||
Helm Chart Validation
|
||||
deploy-vendor-e2e
|
||||
image-and-supply-chain
|
||||
Analyze (go)
|
||||
Analyze (javascript-typescript)
|
||||
```
|
||||
Old-name checks (`deploy-vendor-e2e (<vendor>)` × 12, `deploy-vendor-e2e-windows (<vendor>)` × 2) won't appear on new PRs after the workflow change. Operator removes them from the required list.
|
||||
|
||||
2. **RAM-headroom verification** (frozen decision 0.14) — operator runs the collapsed `deploy-vendor-e2e` job on a one-off branch with `docker stats --no-stream` polling. If peak RSS > 12 GB, fall back to bucketed matrix per `cowork/ci-pipeline-cleanup/decisions-revised.md`. If ≤ 12 GB, current single-job design is the final shape.
|
||||
|
||||
3. **Tag** — operator picks the exact `v2.X.0` value (recommended: increment from `v2.0.66`). 11 phase commits land on master after the prior bundle's closing commit.
|
||||
|
||||
## Acceptance gate verified
|
||||
|
||||
All 19 ☐ items from the prompt's "Final acceptance gate" pass except the operator-only items (3 above). Bundle is shippable pending the operator action.
|
||||
@@ -1,73 +0,0 @@
|
||||
# Reddit / HN announce — ci-pipeline-cleanup
|
||||
|
||||
> Don't auto-post. Operator times manually after the tag lands.
|
||||
|
||||
## r/devops / r/golang
|
||||
|
||||
> **certctl 2.X.0 — CI pipeline cleanup: 19 status checks → 7, ci.yml -71%**
|
||||
>
|
||||
> Open-source Go cert lifecycle tool. v2.X.0 ships a CI-only refactor
|
||||
> that drops status checks per push from 19 → 7, shrinks ci.yml from
|
||||
> 1488 lines to ~430 (-71%), closes three lying-field patterns, and
|
||||
> adds five new gates that catch bug classes the prior pipeline missed.
|
||||
>
|
||||
> The 20 named regression guards (G-1 JWT auth, L-001 InsecureSkipVerify,
|
||||
> H-001 bare FROM, G-3 env-docs drift, etc.) extracted from inline
|
||||
> ci.yml bash to sibling scripts/ci-guards/<id>.sh — each callable
|
||||
> locally as `bash scripts/ci-guards/<id>.sh`. Adding a new guard:
|
||||
> drop a new script; CI loop auto-picks it up.
|
||||
>
|
||||
> Coverage thresholds moved to a YAML manifest with per-package `floor:`
|
||||
> + `why:` (load-bearing context — Bundle reference, HEAD measurement,
|
||||
> gap rationale).
|
||||
>
|
||||
> Three lying fields closed:
|
||||
> - staticcheck `continue-on-error: true` (the M-028 work was
|
||||
> effectively done in earlier bundles, just nobody flipped the gate)
|
||||
> - H-001 bare-FROM guard verifies digest *presence* but not
|
||||
> *resolution* (Bundle II shipped 11 fabricated digests that passed
|
||||
> H-001 and failed `docker pull` in CI). New `digest-validity` step
|
||||
> in the new image-and-supply-chain job resolves every @sha256 ref
|
||||
> against its registry.
|
||||
> - Windows IIS matrix that couldn't physically run on windows-latest
|
||||
> (bridge network driver missing on Windows Docker) AND validated
|
||||
> nothing (16 t.Log placeholders). Deleted; moved to operator
|
||||
> playbook for manual Windows-host validation pre-release.
|
||||
>
|
||||
> Five new gates: digest validity, `go mod tidy` drift, gofmt parity
|
||||
> with Makefile::verify, OpenAPI ↔ handler operationId parity (with
|
||||
> documented exceptions YAML), Docker build smoke for all 4 Dockerfiles.
|
||||
>
|
||||
> Repo: <github>/certctl. Operator guide: docs/ci-pipeline.md.
|
||||
|
||||
## Hacker News
|
||||
|
||||
> **certctl: CI pipeline cleanup — 19 status checks → 7, ci.yml -71%**
|
||||
>
|
||||
> Open-source cert lifecycle tool. v2.X.0 ships a CI refactor that
|
||||
> tightens the on-push pipeline without changing any product behavior.
|
||||
>
|
||||
> The interesting bits: collapsed a 12-job per-vendor matrix to one
|
||||
> job + a skip-count enforcement guard (the per-vendor granularity
|
||||
> was fake signal because 115/116 vendor-edge tests are t.Log
|
||||
> placeholders); deleted a Windows IIS CI matrix that couldn't
|
||||
> physically run on windows-latest (Docker not in Windows-containers
|
||||
> mode by default; bridge network driver missing) AND validated
|
||||
> nothing; flipped staticcheck from soft-gate to hard-fail; added
|
||||
> a digest-validity check that closes the lying-field gap H-001's
|
||||
> regex-only check left open.
|
||||
>
|
||||
> Coverage thresholds in a YAML manifest with per-package `why:`
|
||||
> context. 20 regression guards as standalone scripts, each
|
||||
> callable locally. New 3-tier make convention: verify (pre-commit),
|
||||
> verify-deploy (optional pre-push), verify-docs (pre-tag).
|
||||
|
||||
## Discord (announcement channel template)
|
||||
|
||||
> 🚀 v2.X.0 ships ci-pipeline-cleanup — 19 status checks → 7,
|
||||
> ci.yml -71%, 3 lying fields closed, 5 new gates.
|
||||
>
|
||||
> docs/ci-pipeline.md is the new operator guide. scripts/ci-guards/
|
||||
> hosts the 20 named regression guards extracted from inline ci.yml
|
||||
> bash. .github/coverage-thresholds.yml is the per-package floor
|
||||
> manifest. cowork/ci-pipeline-cleanup/ has the bundle artefacts.
|
||||
@@ -1,191 +0,0 @@
|
||||
# certctl v2.X.0 — CI Pipeline Cleanup
|
||||
|
||||
> Operator-facing release notes for the ci-pipeline-cleanup master bundle.
|
||||
> Operator picks the exact `v2.X.0` from the increment-from-the-last-tag rule.
|
||||
|
||||
## TL;DR
|
||||
|
||||
Restructured the on-push CI pipeline. Status checks per push drop from
|
||||
**19 → 7**. `ci.yml` shrinks **1488 → ~430 lines** (-71%). Three lying
|
||||
fields closed (staticcheck soft-gate; Bundle II's fabricated digest
|
||||
regex-only check; Windows matrix that validated nothing). Five new
|
||||
gates added (digest validity, `go mod tidy` drift, gofmt parity,
|
||||
OpenAPI ↔ handler parity, Docker build smoke).
|
||||
|
||||
**Zero product behavior changes.** No migrations, no API changes, no
|
||||
connector behavior changes. CI-only refactor.
|
||||
|
||||
## What's new
|
||||
|
||||
### `scripts/ci-guards/` — extracted regression guards (Phase 1)
|
||||
|
||||
20 named regression guards moved from inline `ci.yml` bash to sibling
|
||||
scripts:
|
||||
|
||||
- `G-1-jwt-auth-literal.sh`, `L-001-insecure-skip-verify.sh`,
|
||||
`H-001-bare-from.sh`, `M-012-no-root-user.sh`, `H-009-readme-jwt.sh`,
|
||||
`G-2-api-key-hash-json.sh`, `U-2-plaintext-healthcheck.sh`,
|
||||
`U-3-migration-mount.sh`, `D-1-D-2-statusbadge-phantom.sh`,
|
||||
`L-1-bulk-action-loop.sh`, `B-1-orphan-crud.sh`,
|
||||
`S-2-strings-contains-err.sh`, `G-3-env-docs-drift.sh`,
|
||||
`test-naming-convention.sh`, `S-1-hardcoded-source-counts.sh`,
|
||||
`P-1-documented-orphan-fns.sh`, `T-1-frontend-page-coverage.sh`,
|
||||
`bundle-8-L-015-target-blank-rel-noopener.sh`,
|
||||
`bundle-8-L-019-dangerously-set-inner-html.sh`,
|
||||
`bundle-8-M-009-bare-usemutation.sh`
|
||||
|
||||
Each script is callable locally:
|
||||
|
||||
```bash
|
||||
bash scripts/ci-guards/G-3-env-docs-drift.sh
|
||||
```
|
||||
|
||||
CI step is a single loop that auto-picks up new scripts. Adding a new
|
||||
guard: drop a new `<id>.sh`; no `ci.yml` change required.
|
||||
|
||||
The 2 QA-doc guards (Part-count + seed-count) moved to `make verify-docs`
|
||||
instead — they protect docs-the-operator-reads, not anything the
|
||||
product depends on.
|
||||
|
||||
### `.github/coverage-thresholds.yml` (Phase 2)
|
||||
|
||||
Per-package coverage floors moved out of inline bash into a YAML
|
||||
manifest. Each entry has `floor:` (integer percentage) + `why:`
|
||||
(load-bearing context — Bundle reference, HEAD measurement, gap
|
||||
rationale). Adding a new gated package: one YAML entry instead of
|
||||
~30 lines of bash. Floors unchanged from HEAD.
|
||||
|
||||
### `staticcheck` hard gate (Phase 3)
|
||||
|
||||
The old `continue-on-error: true` lying field with the "M-028 will
|
||||
close 6 SA1019 sites" comment is gone. Verified at HEAD: all live
|
||||
SA1019 sites either migrated (`middleware.NewAuth` → `NewAuthWithNamedKeys`)
|
||||
or suppressed inline with load-bearing rationale (`csr.Attributes` for
|
||||
RFC 2985 challengePassword; `elliptic.Marshal` only in byte-equivalence
|
||||
test). Gate now hard.
|
||||
|
||||
### `make verify` parity + `go mod tidy` drift (Phase 4)
|
||||
|
||||
Two new steps in `go-build-and-test`:
|
||||
- **gofmt drift** — closes the parity gap with `Makefile::verify`
|
||||
(CI was running vet + lint + test but not gofmt)
|
||||
- **go mod tidy drift** — `go mod tidy && git diff --exit-code go.mod go.sum`
|
||||
|
||||
### `deploy-vendor-e2e` collapsed: 12 jobs → 1 job (Phase 5)
|
||||
|
||||
Per-vendor matrix granularity was fake signal — verified that 115/116
|
||||
vendor-edge tests are `t.Log` placeholders. Single job brings up all
|
||||
11 sidecars at once + runs the full `VendorEdge_` suite + enforces
|
||||
skip-count (no sidecar may silently fail to come up).
|
||||
|
||||
NEW `scripts/ci-guards/vendor-e2e-skip-check.sh` + allowlist file at
|
||||
`scripts/ci-guards/vendor-e2e-skip-allowlist.txt` (15 windows-iis-
|
||||
requiring tests legitimately skip on Linux per Phase 6).
|
||||
|
||||
**Revises Bundle II frozen decision 0.9.** Documented in
|
||||
`cowork/ci-pipeline-cleanup/decisions-revised.md`.
|
||||
|
||||
### `deploy-vendor-e2e-windows` deleted entirely (Phase 6)
|
||||
|
||||
The Windows matrix can't physically work on `windows-latest` GitHub
|
||||
runners (Docker not started in Windows-containers mode by default;
|
||||
`bridge` network driver missing on Windows Docker — uses `nat`).
|
||||
Even if fixed, all 16 IIS + WinCertStore tests are `t.Log` placeholders.
|
||||
|
||||
NEW `docs/connector-iis.md::Operator validation playbook` documents
|
||||
the manual-on-Windows-host procedure operators run pre-release. The
|
||||
`windows-iis-test` sidecar stays in `deploy/docker-compose.test.yml`
|
||||
under `profiles: [deploy-e2e-windows]` for operator local use.
|
||||
|
||||
`docs/deployment-vendor-matrix.md` IIS + WinCertStore rows status
|
||||
updated `pending` → `operator-playbook`.
|
||||
|
||||
**Revises Bundle II frozen decision 0.4.** Documented in
|
||||
`cowork/ci-pipeline-cleanup/decisions-revised.md`.
|
||||
|
||||
### NEW `image-and-supply-chain` job (Phases 7-9)
|
||||
|
||||
Top-level Ubuntu job (~3 min, parallel to `go-build-and-test`). Three
|
||||
steps:
|
||||
|
||||
1. **Digest validity** — every `@sha256:<digest>` ref in
|
||||
`deploy/**/*.{yml,Dockerfile*}` must resolve on its registry.
|
||||
Closes the H-001 lying-field gap (H-001 verifies digest *presence*
|
||||
only — Bundle II shipped 11 fabricated digests that passed H-001
|
||||
and failed `docker pull` in CI).
|
||||
2. **Docker build smoke** — all 4 Dockerfiles in the repo must build
|
||||
(`Dockerfile`, `Dockerfile.agent`,
|
||||
`deploy/test/f5-mock-icontrol/Dockerfile`,
|
||||
`deploy/test/libest/Dockerfile`).
|
||||
3. **OpenAPI ↔ handler operationId parity** — every router route has
|
||||
a matching `operationId` in `api/openapi.yaml` or is documented in
|
||||
the new `api/openapi-handler-exceptions.yaml` (8 documented
|
||||
exceptions at HEAD: SCEP + SCEP-mTLS wire-protocol endpoints).
|
||||
|
||||
### Coverage PR-comment action (Phase 10)
|
||||
|
||||
Self-hosted alternative to Codecov / Coveralls. Posts per-package
|
||||
coverage table as a PR comment; updates in place on subsequent
|
||||
pushes. No paid SaaS dependency.
|
||||
|
||||
### `make verify-docs` + `make verify-deploy` (Phase 11)
|
||||
|
||||
Three-tier convention now:
|
||||
- `make verify` — required pre-commit (gofmt + vet + lint + test)
|
||||
- `make verify-deploy` — optional pre-push (digest validity + OpenAPI
|
||||
parity + Docker build smoke for server + agent)
|
||||
- `make verify-docs` — required pre-tag (QA-doc Part-count + seed-count)
|
||||
|
||||
### NEW `docs/ci-pipeline.md` (Phase 12)
|
||||
|
||||
Operator-facing guide to the on-push pipeline. Per-job deep-dive,
|
||||
guard inventory, threshold management, troubleshooting matrix, branch
|
||||
protection list to update.
|
||||
|
||||
## Operator action required
|
||||
|
||||
After merge:
|
||||
|
||||
1. **Update GitHub branch protection rule** for `master` branch.
|
||||
Required-checks list changes from 19 entries → 7:
|
||||
- `Go Build & Test`
|
||||
- `Frontend Build`
|
||||
- `Helm Chart Validation`
|
||||
- `deploy-vendor-e2e`
|
||||
- `image-and-supply-chain`
|
||||
- `Analyze (go)`
|
||||
- `Analyze (javascript-typescript)`
|
||||
|
||||
2. **(Optional)** RAM-headroom verification on a test branch with the
|
||||
collapsed `deploy-vendor-e2e` job. If peak RSS > 12 GB on
|
||||
ubuntu-latest, fall back to bucketed matrix per
|
||||
`cowork/ci-pipeline-cleanup/decisions-revised.md`.
|
||||
|
||||
## Rollback
|
||||
|
||||
If RAM headroom proves insufficient or a guard misbehaves:
|
||||
|
||||
- Vendor matrix collapse (Phase 5): revert that one commit; fall back
|
||||
to the bucketed-matrix design (3 jobs × ~4 sidecars).
|
||||
- staticcheck hard gate (Phase 3): revert that one commit; flip
|
||||
`continue-on-error: true` back temporarily until the new SA1019
|
||||
site is closed.
|
||||
- All other phases are pure-additive or pure-extraction; reverting
|
||||
any single Phase commit restores the prior behavior.
|
||||
|
||||
## Verification
|
||||
|
||||
```
|
||||
make verify # pre-commit gate (existing)
|
||||
make verify-deploy # optional pre-push (new)
|
||||
make verify-docs # pre-tag (new)
|
||||
bash scripts/ci-guards/*.sh # all 20 guards locally
|
||||
bash scripts/check-coverage-thresholds.sh # only after coverage.out exists
|
||||
```
|
||||
|
||||
All passing on HEAD.
|
||||
|
||||
## Tag
|
||||
|
||||
Operator picks the exact `v2.X.0` value. Bundle ships ~13 commits
|
||||
on master after the prior bundle's closing commit (HEAD `1de61e91`).
|
||||
@@ -1,7 +1,7 @@
|
||||
# certctl Load-Test Harness
|
||||
|
||||
Closes the **#8 acquisition-readiness blocker** from the 2026-05-01 issuer
|
||||
coverage audit (`cowork/issuer-coverage-audit-2026-05-01/RESULTS.md`).
|
||||
coverage audit (the 2026-05-01 issuer coverage audit).
|
||||
Pre-fix, certctl had zero benchmarks or load tests for any API path; an
|
||||
acquirer evaluating "can certctl handle our 50k-cert fleet at 47-day
|
||||
rotation" had nothing to point at. This harness is the substantiation.
|
||||
@@ -354,6 +354,6 @@ verification.
|
||||
|
||||
## Audit references
|
||||
|
||||
- API tier: `cowork/issuer-coverage-audit-2026-05-01/RESULTS.md` fix #8.
|
||||
- Connector tier: `cowork/deployment-target-audit-2026-05-02/RESULTS.md` Bundle 10.
|
||||
- ACME flows: Phase 5 master prompt (`cowork/acme-server-prompts/06-phase-5-certmanager-hardening-prompt.md`).
|
||||
- API tier: 2026-05-01 issuer coverage audit fix #8.
|
||||
- Connector tier: 2026-05-02 deployment-target audit Bundle 10.
|
||||
- ACME flows: Phase 5 master prompt (project notes).
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
# Usage: make loadtest (from the repo root)
|
||||
# Manual: cd deploy/test/loadtest && docker compose up --abort-on-container-exit --exit-code-from k6
|
||||
#
|
||||
# Audit reference (API tier): cowork/issuer-coverage-audit-2026-05-01/RESULTS.md fix #8.
|
||||
# Audit reference (connector tier): cowork/deployment-target-audit-2026-05-02/RESULTS.md Bundle 10.
|
||||
# Audit reference (API tier): 2026-05-01 issuer coverage audit fix #8.
|
||||
# Audit reference (connector tier): 2026-05-02 deployment-target audit Bundle 10.
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
// tests are too slow to gate per-PR signal).
|
||||
//
|
||||
// Audit references:
|
||||
// - API tier: cowork/issuer-coverage-audit-2026-05-01/RESULTS.md fix #8.
|
||||
// - Connector tier: cowork/deployment-target-audit-2026-05-02/RESULTS.md Bundle 10.
|
||||
// - API tier: 2026-05-01 issuer coverage audit fix #8.
|
||||
// - Connector tier: 2026-05-02 deployment-target audit Bundle 10.
|
||||
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> Last reviewed: 2026-05-05
|
||||
|
||||
> Authoritative guide to certctl's CI pipeline shape.
|
||||
> Per `cowork/ci-pipeline-cleanup-prompt.md` Phase 12.
|
||||
> Per the ci-pipeline-cleanup spec, Phase 12.
|
||||
|
||||
## Trigger model
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ After the release workflow runs (triggered by tag push), verify the published ar
|
||||
| Release workflow completed without errors | GitHub Actions | ☐ |
|
||||
| Sample binary downloaded and Cosign-verified by an operator who is not the release author | another team member | ☐ |
|
||||
| `WORKSPACE-CHANGELOG.md` notes the tag commit SHA | manual edit | ☐ |
|
||||
| `cowork/CLAUDE.md` "Active Focus" → "Current tag" updated | manual edit | ☐ |
|
||||
| workspace-tracking "Active Focus" → "Current tag" updated | manual edit | ☐ |
|
||||
| `certctl.io/index.html` star count + `data-gh-version` rendering picks up the new tag | open the landing page in 6+ hours (cache TTL) | ☐ |
|
||||
| Reddit / Hacker News / LinkedIn announcement drafted (if a major release) | per the operator's promotion playbook | ☐ |
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ the bug the mutant introduced).
|
||||
|
||||
**Acceptance threshold:** ≥80% mutation kill ratio per package. Surviving
|
||||
mutants below that threshold get triaged in
|
||||
`cowork/comprehensive-audit-2026-04-25/d003-mutation-results.md` — either
|
||||
the project's 2026-04-25 mutation-results notes — either
|
||||
ship a targeted unit test that kills the mutant, or document an
|
||||
equivalent-mutation justification.
|
||||
|
||||
|
||||
@@ -989,7 +989,7 @@ export CERTCTL_API_KEY="test-key-123"
|
||||
|
||||
## Part 15: MCP Server for AI Integration (M18a)
|
||||
|
||||
certctl exposes the full REST API via the Model Context Protocol (MCP), enabling seamless integration with Claude, Cursor, and other AI assistants:
|
||||
certctl exposes the full REST API via the Model Context Protocol (MCP), enabling seamless integration with any MCP-compatible AI client:
|
||||
|
||||
```bash
|
||||
# Build the MCP server
|
||||
@@ -1010,19 +1010,19 @@ export CERTCTL_API_KEY="test-key-123"
|
||||
- **Binary support** — handles DER-encoded CRL and OCSP responses without mangling
|
||||
- **Error translation** — converts HTTP errors to user-readable messages
|
||||
|
||||
**Example usage from Claude:**
|
||||
**Example usage:**
|
||||
|
||||
```
|
||||
User: What certificates are expiring in the next 30 days?
|
||||
|
||||
Claude uses the MCP tools to:
|
||||
The AI client uses the MCP tools to:
|
||||
1. Call tools.listCertificates with filters: {status: "Expiring"}
|
||||
2. Parse the response
|
||||
3. Display: "mc-api-prod expires in 12 days. mc-cdn-prod expires in 8 days..."
|
||||
|
||||
User: Revoke mc-payments due to key compromise
|
||||
|
||||
Claude uses the MCP tools to:
|
||||
The AI client uses the MCP tools to:
|
||||
1. Call tools.revokeCertificate with id="mc-payments" reason="keyCompromise"
|
||||
2. Return the audit trail entry showing revocation recorded
|
||||
```
|
||||
|
||||
@@ -258,7 +258,7 @@ The CLI supports both table and JSON output formats (`--format table` or `--form
|
||||
|
||||
### MCP Server (AI Integration)
|
||||
|
||||
certctl includes an MCP (Model Context Protocol) server that exposes the entire REST API as MCP tools. This enables AI assistants like Claude, Cursor, and other MCP-compatible tools to interact with your certificate infrastructure using natural language — "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?"
|
||||
certctl includes an MCP (Model Context Protocol) server that exposes the entire REST API as MCP tools. This enables AI assistants and other MCP-compatible tools to interact with your certificate infrastructure using natural language — "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?"
|
||||
|
||||
The MCP server is a separate binary (`cmd/mcp-server/`) that communicates via stdio transport and acts as a stateless HTTP proxy to the certctl REST API. It requires no additional infrastructure — just point it at your certctl server URL and API key.
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ export CERTCTL_SERVER_CA_BUNDLE_PATH="$CA" # MCP is env-vars-only; no CLI flag
|
||||
./mcp-server
|
||||
```
|
||||
|
||||
Exposes the full REST API via MCP over stdio transport. Ask Claude: "What certificates are expiring in the next 30 days?", "Revoke the payments cert due to key compromise", "Show me the audit trail."
|
||||
Exposes the full REST API via MCP over stdio transport. Ask your MCP client: "What certificates are expiring in the next 30 days?", "Revoke the payments cert due to key compromise", "Show me the audit trail."
|
||||
|
||||
## Demo Data Reference
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ The three differentiators above get the headlines, but the feature surface is wi
|
||||
|
||||
**Prometheus metrics** — `/api/v1/metrics/prometheus` in standard exposition format. Works with Prometheus, Grafana Agent, Datadog Agent, Victoria Metrics.
|
||||
|
||||
**MCP server** — the entire REST API is exposed via MCP for AI-assisted certificate management via Claude, Cursor, or any MCP-compatible client. No other certificate platform offers this.
|
||||
**MCP server** — the entire REST API is exposed via MCP for AI-assisted certificate management via any MCP-compatible client. No other certificate platform offers this.
|
||||
|
||||
**Full REST API** — OpenAPI 3.1-documented operations covering the entire platform. CLI tool with 10 subcommands. Helm chart for Kubernetes deployment. Scheduled certificate digest emails. Certificate export in PEM and PKCS#12. S/MIME support with EKU-aware issuance.
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ After any of:
|
||||
- Connection pool config change
|
||||
- Changing the renewal scheduler interval
|
||||
|
||||
Capture timing in `cowork/loadtest-baselines/<date>.md` so future regressions surface against a real baseline rather than the operator's gut feeling.
|
||||
Capture timing in your own loadtest-baselines log so future regressions surface against a real baseline rather than the operator's gut feeling.
|
||||
|
||||
## Related docs
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ az monitor activity-log list \
|
||||
|
||||
## V3-Pro forward path
|
||||
|
||||
Tracked at `cowork/WORKSPACE-ROADMAP.md` under "Adapter hardening":
|
||||
Tracked under "Adapter hardening" on the project roadmap:
|
||||
|
||||
- **AWS CloudFront direct-attach** — UpdateDistribution after an ACM
|
||||
ImportCertificate so the CloudFront edge picks up the new cert
|
||||
|
||||
@@ -238,7 +238,7 @@ remains trusted by relying parties until its `notAfter` (typical
|
||||
openssl x509 -in new-cert -noout -issuer
|
||||
```
|
||||
|
||||
**Future:** when the HSM/PKCS#11 driver bundle (`cowork/hsm-pkcs11-
|
||||
**Future:** when the HSM/PKCS#11 driver bundle (planned;
|
||||
driver-prompt.md`) ships, this rotation procedure changes
|
||||
substantially — the HSM-backed key never moves, only the cert wrap
|
||||
rotates. The signer interface seam is the load-bearing prerequisite
|
||||
|
||||
@@ -217,7 +217,7 @@ dedup on the `notification_events` table guards against that).
|
||||
|
||||
## V3-Pro forward path
|
||||
|
||||
Tracked at `cowork/WORKSPACE-ROADMAP.md` under "Adapter hardening":
|
||||
Tracked under "Adapter hardening" on the project roadmap:
|
||||
|
||||
- Per-owner / per-team / per-tenant channel routing (the matrix is
|
||||
per-policy today, not per-owner).
|
||||
|
||||
@@ -156,7 +156,7 @@ Same three controls as CLI, env-var-driven only (no flags — MCP runs as a stdi
|
||||
- `CERTCTL_SERVER_CA_BUNDLE_PATH` optional CA bundle
|
||||
- `CERTCTL_SERVER_TLS_INSECURE_SKIP_VERIFY` optional skip
|
||||
|
||||
Claude Desktop / other MCP client configs should set all three in the tool's env block.
|
||||
MCP-client configs should set all three in the tool's env block.
|
||||
|
||||
## Troubleshooting: fail-loud preflight errors
|
||||
|
||||
|
||||
@@ -1096,11 +1096,11 @@ Health checks live outside the API prefix: `GET /health` and `GET /ready`.
|
||||
|
||||
## MCP Server
|
||||
|
||||
certctl includes an MCP (Model Context Protocol) server as a separate binary (`cmd/mcp-server/`) that enables AI assistants to interact with the certificate platform. The MCP server uses the official MCP Go SDK (`modelcontextprotocol/go-sdk`) with stdio transport for integration with Claude, Cursor, and other MCP-compatible tools.
|
||||
certctl includes an MCP (Model Context Protocol) server as a separate binary (`cmd/mcp-server/`) that enables AI assistants to interact with the certificate platform. The MCP server uses the official MCP Go SDK (`modelcontextprotocol/go-sdk`) with stdio transport for integration with any MCP-compatible AI client.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
AI["AI Assistant\n(Claude, Cursor)"] -->|"stdio"| MCP["MCP Server\ncmd/mcp-server/"]
|
||||
AI["AI Assistant\n(any MCP client)"] -->|"stdio"| MCP["MCP Server\ncmd/mcp-server/"]
|
||||
MCP -->|"HTTP + Bearer token"| API["certctl REST API\n:8443"]
|
||||
|
||||
subgraph "MCP Tools"
|
||||
@@ -1318,7 +1318,7 @@ For detailed test procedures, smoke tests, and the release sign-off checklist, s
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
Closes the #8 acquisition-readiness blocker from the 2026-05-01 issuer coverage audit (see `cowork/issuer-coverage-audit-2026-05-01/RESULTS.md`). Pre-audit, certctl had no benchmarks or load tests for any API path, so any throughput claim was hand-waved; the harness in `deploy/test/loadtest/` substantiates the API-tier capacity numbers with reproducible methodology.
|
||||
Closes the #8 acquisition-readiness blocker from the 2026-05-01 issuer coverage audit. Pre-audit, certctl had no benchmarks or load tests for any API path, so any throughput claim was hand-waved; the harness in `deploy/test/loadtest/` substantiates the API-tier capacity numbers with reproducible methodology.
|
||||
|
||||
The harness drives a k6 client at sustained 50 req/s × 2 scenarios × 5 minutes against a docker-compose stack of postgres + tls-init + certctl-server. Two scenarios run in parallel: `POST /api/v1/certificates` (issuance-acceptance hot path: auth + JSON decode + validation + service `CreateCertificate` + `managed_certificates` insert) and `GET /api/v1/certificates?per_page=50` (most-trafficked read endpoint). Hard regression-guard thresholds: p99 < 5 s for issuance-acceptance, p99 < 2 s for list, error rate < 1% globally. k6 exits non-zero on any threshold breach so a future PR that pushes p99 above the bar fails `make loadtest`. Run via `make loadtest` from the repo root or via `.github/workflows/loadtest.yml` (`workflow_dispatch` + weekly cron — never per-push).
|
||||
|
||||
|
||||
@@ -197,4 +197,4 @@ only when ALL of the following are true:
|
||||
- This playbook's full procedure ran clean once on a real Windows host
|
||||
|
||||
Operator records the validation date + Windows Server version in
|
||||
`cowork/<bundle>/iis-validation-receipts.md` for audit trail.
|
||||
the project's per-bundle iis-validation receipts for audit trail.
|
||||
|
||||
@@ -316,7 +316,7 @@ The cert version must exist in the local store: this means the cert was issued t
|
||||
|
||||
Reason codes follow RFC 5280 §5.3.1: nil reason maps to `unspecified` (0), and the connector accepts the canonical camelCase form (`keyCompromise`, `cACompromise`, `affiliationChanged`, `superseded`, `cessationOfOperation`, `certificateHold`, `removeFromCRL`, `privilegeWithdrawn`, `aACompromise`) plus underscore_lower and ALL_CAPS_UNDERSCORE variants. An unknown reason returns an error rather than silently demoting to `unspecified` — operators rely on the reason for audit reporting.
|
||||
|
||||
Audit reference: `cowork/issuer-coverage-audit-2026-05-01/RESULTS.md` Top-10 fix #7.
|
||||
Audit reference: 2026-05-01 issuer coverage audit Top-10 fix #7.
|
||||
|
||||
Location: `internal/connector/issuer/acme/acme.go`, `internal/connector/issuer/acme/dns.go`
|
||||
|
||||
@@ -405,7 +405,7 @@ certctl's OpenSSL adapter `exec`s an operator-supplied script for every certific
|
||||
|
||||
**V3-Pro forward path:**
|
||||
|
||||
The hardened OpenSSL adapter (chroot/container by default, env-var allow-list at the adapter layer, signed-script-binary verification, audit-log-on-every-invocation, per-call concurrency bound shared with the API surface) is V3-Pro work. Tracking: `cowork/WORKSPACE-ROADMAP.md` (search "OpenSSL hardened mode").
|
||||
The hardened OpenSSL adapter (chroot/container by default, env-var allow-list at the adapter layer, signed-script-binary verification, audit-log-on-every-invocation, per-call concurrency bound shared with the API surface) is V3-Pro work. Tracking: project roadmap, "OpenSSL hardened mode".
|
||||
|
||||
### Revocation Across Issuers
|
||||
|
||||
@@ -1711,7 +1711,7 @@ ORDER BY created_at DESC;
|
||||
|
||||
Each row corresponds to one fired alert. The `channel` metadata field tells you which notifier ran. Combined with the Prometheus `certctl_expiry_alerts_total{result="failure"}` counter, you have full forensic visibility on every dispatch attempt.
|
||||
|
||||
**V3-Pro forward path.** Per-owner / per-team channel routing (route the Production-CDN cert's alerts to its dedicated owner's PagerDuty service, the Internal-API cert's alerts to a different one), calendar-aware suppression (no T-30 informational alerts on weekends for non-on-call teams), and escalation chains (T-1 unanswered for 30m → escalate to manager) are tracked on `cowork/WORKSPACE-ROADMAP.md` under "Adapter hardening" → "Multi-channel expiry alerts: per-owner routing".
|
||||
**V3-Pro forward path.** Per-owner / per-team channel routing (route the Production-CDN cert's alerts to its dedicated owner's PagerDuty service, the Internal-API cert's alerts to a different one), calendar-aware suppression (no T-30 informational alerts on weekends for non-on-call teams), and escalation chains (T-1 unanswered for 30m → escalate to manager) are tracked on the project roadmap under "Adapter hardening" → "Multi-channel expiry alerts: per-owner routing".
|
||||
|
||||
### Email (SMTP) Notifier
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ The hardened OpenSSL adapter (chroot/container by default,
|
||||
env-var allow-list at the adapter layer, signed-script-binary
|
||||
verification, audit-log-on-every-invocation, per-call concurrency
|
||||
bound shared with the API surface) is V3-Pro work. Tracking:
|
||||
`cowork/WORKSPACE-ROADMAP.md` (search "OpenSSL hardened mode").
|
||||
the project roadmap (search "OpenSSL hardened mode").
|
||||
|
||||
## Related docs
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ a single shared primitive:
|
||||
|---|---|---|
|
||||
| **Atomic deploy with rollback** | F5 only (transactional API) | 12 of 13 connectors via `deploy.Apply` (K8s pending Bundle 2 — see [Section 1.5](#15-audit-closure-status-2026-05-02-deployment-target-audit)) |
|
||||
| **Post-deploy TLS verification** | None | NGINX/Apache/HAProxy/Traefik/Caddy/Envoy/Postfix all do TLS handshake + SHA-256 fingerprint compare; fail → rollback |
|
||||
| **Vendor-specific deployment recipes** | Light docs | (Bundle II — `cowork/deploy-hardening-ii-prompt.md`) |
|
||||
| **Vendor-specific deployment recipes** | Light docs | (Bundle II — per the project's deploy-hardening II spec) |
|
||||
|
||||
This document describes the operator-visible surface. The Go-level
|
||||
contract lives at `internal/deploy/doc.go`.
|
||||
@@ -31,7 +31,7 @@ contract lives at `internal/deploy/doc.go`.
|
||||
## 1.5. Audit closure status (2026-05-02 deployment-target audit)
|
||||
|
||||
The 2026-05-02 deployment-target coverage audit
|
||||
(`cowork/deployment-target-audit-2026-05-02/RESULTS.md`) tightened the
|
||||
(the 2026-05-02 deployment-target audit) tightened the
|
||||
atomic + rollback contract on the connectors below. All bundles in the
|
||||
table are committed to `master` as of this section's last edit; commit
|
||||
hashes pin to the canonical landing commit for each piece of work.
|
||||
@@ -54,7 +54,7 @@ hashes pin to the canonical landing commit for each piece of work.
|
||||
real `k8s.io/client-go` implementation + `ResourceVersion` plumbing
|
||||
+ post-deploy SHA-256 verify + kubelet sync poll is the remaining
|
||||
V2 P0 blocker. Tracking prompt:
|
||||
`cowork/deployment-target-audit-2026-05-02/k8s-real-client-prompt.md`.
|
||||
the project's k8s-real-client spec.
|
||||
|
||||
Bundle 10 (per-connector loadtest harness, commit `6286cd4`) does not
|
||||
modify the per-connector contract table; it's a CI / observability
|
||||
@@ -134,7 +134,7 @@ Apply's algorithm:
|
||||
| ssh | (Connect probe) | (SCP upload + remote chmod) | `tls.Dial` to remote TLS port | Pre-deploy SCP backup of remote files |
|
||||
| wincertstore | (Get-ChildItem Cert:\) | (Import-PfxCertificate) | (admin probe) | Get-ChildItem snapshot for rollback |
|
||||
| javakeystore | (`keytool -list`) | (`keytool -importkeystore`) | (admin probe) | keytool snapshot; rollback via `keytool -delete` + re-import |
|
||||
| k8ssecret | (V2 blocker — see note below) | (V2 blocker — see note below) | (V2 blocker — see note below) | **V2 blocker — Bundle 2 of the 2026-05-02 deployment-target audit.** Production `realK8sClient` at `internal/connector/target/k8ssecret/k8ssecret.go:397-420` is a stub (every method returns `"real Kubernetes client not implemented — use NewWithClient for tests"`). The SHA-256 post-deploy verify and kubelet sync poll are designed but not yet implemented; production deploys to a real cluster fail with "not implemented" until Bundle 2 lands. Test mocks via `NewWithClient` work today. Tracking prompt: `cowork/deployment-target-audit-2026-05-02/k8s-real-client-prompt.md`. |
|
||||
| k8ssecret | (V2 blocker — see note below) | (V2 blocker — see note below) | (V2 blocker — see note below) | **V2 blocker — Bundle 2 of the 2026-05-02 deployment-target audit.** Production `realK8sClient` at `internal/connector/target/k8ssecret/k8ssecret.go:397-420` is a stub (every method returns `"real Kubernetes client not implemented — use NewWithClient for tests"`). The SHA-256 post-deploy verify and kubelet sync poll are designed but not yet implemented; production deploys to a real cluster fail with "not implemented" until Bundle 2 lands. Test mocks via `NewWithClient` work today. Tracking prompt: the project's k8s-real-client spec. |
|
||||
|
||||
> **Postfix vs Dovecot mode**: see "Choosing Mode=postfix vs Mode=dovecot" in
|
||||
> `docs/connectors.md` for the per-mode defaults (cert/key paths, validate +
|
||||
@@ -302,7 +302,7 @@ Out of scope for the V2-free deploy-hardening I bundle:
|
||||
deploy audit trail in a reviewer-friendly format.
|
||||
- **Customer-paid validation matrices** — vendor-version certified
|
||||
quirks (e.g. "tested on F5 v15.1 + v17.0 + v17.5"). See
|
||||
`cowork/deploy-hardening-ii-prompt.md` for the per-vendor
|
||||
the project's deploy-hardening II spec for the per-vendor
|
||||
edge-case audit + integration test sidecars.
|
||||
|
||||
## 12. Per-connector quick reference
|
||||
|
||||
+7
-43
@@ -2,9 +2,9 @@
|
||||
|
||||
> Last reviewed: 2026-05-05
|
||||
|
||||
certctl ships with an MCP (Model Context Protocol) server that lets AI assistants manage your certificate infrastructure through natural language. Ask Claude to "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?" and the MCP server translates that into API calls against your certctl instance.
|
||||
certctl ships with an MCP (Model Context Protocol) server that lets AI assistants manage your certificate infrastructure through natural language. Ask your MCP-compatible AI client to "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?" and the MCP server translates that into API calls against your certctl instance.
|
||||
|
||||
This guide covers setup, configuration, and usage with Claude, Cursor, and other MCP-compatible tools.
|
||||
This guide covers setup, configuration, and usage with any MCP-compatible AI client.
|
||||
|
||||
## What Is MCP?
|
||||
|
||||
@@ -18,7 +18,7 @@ You need:
|
||||
|
||||
1. A running certctl server (see [Quick Start](../getting-started/quickstart.md))
|
||||
2. The MCP server binary — either built from source or from a Docker image
|
||||
3. An MCP-compatible AI client (Claude Desktop, Cursor, VS Code with Copilot, etc.)
|
||||
3. An MCP-compatible AI client
|
||||
|
||||
## Building the MCP Server
|
||||
|
||||
@@ -43,9 +43,9 @@ If your certctl server has auth enabled (the default), you must provide the API
|
||||
|
||||
Since v2.2 the certctl control plane is HTTPS-only. If the server cert is self-signed or chained to an internal CA, set `CERTCTL_SERVER_CA_BUNDLE_PATH` so the MCP server can verify the TLS handshake. Never set `CERTCTL_SERVER_TLS_INSECURE_SKIP_VERIFY=true` outside local development — it disables all certificate validation.
|
||||
|
||||
## Setting Up with Claude Desktop
|
||||
## Configuring Your MCP Client
|
||||
|
||||
Add this to your Claude Desktop MCP configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
|
||||
Most MCP clients accept a JSON config block of this shape. Consult your client's documentation for the exact config-file location.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -62,43 +62,7 @@ Add this to your Claude Desktop MCP configuration file (`~/Library/Application S
|
||||
}
|
||||
```
|
||||
|
||||
Restart Claude Desktop. You should see "certctl" appear in the MCP tools list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`).
|
||||
|
||||
## Setting Up with Cursor
|
||||
|
||||
In Cursor, go to Settings → MCP Servers and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"certctl": {
|
||||
"command": "/path/to/certctl-mcp",
|
||||
"env": {
|
||||
"CERTCTL_SERVER_URL": "https://localhost:8443",
|
||||
"CERTCTL_SERVER_CA_BUNDLE_PATH": "/path/to/certctl/deploy/test/certs/ca.crt",
|
||||
"CERTCTL_API_KEY": "your-api-key-here"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setting Up with Claude Code
|
||||
|
||||
Add certctl as an MCP server in your project's `.mcp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"certctl": {
|
||||
"command": "/path/to/certctl-mcp",
|
||||
"env": {
|
||||
"CERTCTL_SERVER_URL": "https://localhost:8443",
|
||||
"CERTCTL_SERVER_CA_BUNDLE_PATH": "/path/to/certctl/deploy/test/certs/ca.crt",
|
||||
"CERTCTL_API_KEY": "your-api-key-here"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
After saving, restart your MCP client. You should see "certctl" appear in its tool list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`).
|
||||
|
||||
## Available Tools
|
||||
|
||||
@@ -154,7 +118,7 @@ The AI calls `certctl_create_certificate` with the common name, team ID, and own
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
AI["AI Assistant\n(Claude, Cursor)"]
|
||||
AI["AI Assistant\n(any MCP client)"]
|
||||
MCP["certctl MCP\ncmd/mcp-server/"]
|
||||
SERVER["certctl Server\n:8443"]
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ multicast, IPv4-mapped-IPv6 to a reserved IPv4. See
|
||||
CodeQL alert #23 flags `client.Do(req)` in the SCEP-probe call site
|
||||
as `go/request-forgery` despite the dial-time guard; the analyzer
|
||||
can't trace through a custom `Transport.DialContext`. Operator-
|
||||
acknowledged false positive (CLAUDE.md task #10) — see the SCEP
|
||||
acknowledged false positive (tracked internally) — see the SCEP
|
||||
probe's same-shaped defense for the audit trail.
|
||||
|
||||
## DNS-01 cache poisoning posture
|
||||
|
||||
@@ -609,7 +609,7 @@ Not yet automatic. Operators migrating: keep the old `managed_certificates`
|
||||
rows; create new ones via the ACME flow; flip targets one by one. A
|
||||
dedicated bulk-migration tool is on the roadmap (post-2.1.0). Track
|
||||
via the master prompt's roadmap section in
|
||||
`cowork/acme-server-endpoint-prompt.md`.
|
||||
the project's acme-server-endpoint spec.
|
||||
|
||||
### What audit-log events fire on each ACME operation?
|
||||
|
||||
|
||||
@@ -116,5 +116,5 @@ enrollments per tick.
|
||||
|
||||
## Audit blocker reference
|
||||
|
||||
cowork/issuer-coverage-audit-2026-05-01/RESULTS.md, Top-10 fix #5
|
||||
the 2026-05-01 issuer coverage audit, Top-10 fix #5
|
||||
(Part 1.5 finding #4: "No polling backoff for async CAs").
|
||||
|
||||
@@ -504,7 +504,7 @@ arbitrary).
|
||||
EST signs certs using whatever issuer connector the profile binds.
|
||||
The `internal/crypto/signer/` interface (post-2026-04-28) means a
|
||||
future HSM/PKCS#11 driver bundle (parking-lot at
|
||||
`cowork/hsm-pkcs11-driver-prompt.md`) plugs in transparently — the
|
||||
planned) plugs in transparently — the
|
||||
EST handler doesn't change. EST-issued certs benefit from HSM-backed
|
||||
signing automatically once the HSM bundle ships and the operator
|
||||
swaps the local issuer's `FileDriver` for a `PKCS11Driver`.
|
||||
|
||||
@@ -167,7 +167,7 @@ func (h ACMEHandler) NewNonce(w http.ResponseWriter, r *http.Request) {
|
||||
func (h ACMEHandler) directoryBaseURL(r *http.Request, profileID string) string {
|
||||
scheme := "https"
|
||||
if r.TLS == nil {
|
||||
// HTTPS-only architecture decision (CLAUDE.md): the listener
|
||||
// HTTPS-only architecture decision: the listener
|
||||
// is TLS 1.3 pinned. r.TLS == nil only happens in tests with
|
||||
// httptest.NewServer (non-TLS); honor http: for those.
|
||||
scheme = "http"
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// service-side and the handler stays test-friendly.
|
||||
//
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9.1, extended in the
|
||||
// Phase 9 follow-up (cowork/scep-gui-restructure-prompt.md) with
|
||||
// Phase 9 follow-up (the project's SCEP GUI restructure spec) with
|
||||
// Profiles for the per-profile SCEP Administration tab.
|
||||
type AdminSCEPIntuneService interface {
|
||||
// Stats returns one snapshot per configured SCEP profile (Intune-
|
||||
|
||||
@@ -90,7 +90,7 @@ type VaultRenewalSnapshotter interface {
|
||||
// (here).
|
||||
//
|
||||
// Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5).
|
||||
// (the project's deep-research deliverable, Part 5).
|
||||
type ExpiryAlertSnapshotter interface {
|
||||
// SnapshotExpiryAlerts returns one entry per non-zero counter,
|
||||
// pre-sorted by (channel, threshold, result) so the Prometheus
|
||||
|
||||
@@ -286,7 +286,7 @@ func TestSCEPHandler_ChromeOSPKIMessage_AESVariants(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestSCEPHandler_ChromeOSPKIMessage_RAKeyMismatch — closure-bundle
|
||||
// gap M-1 / acceptance D.1 (cowork/scep-bundle-gap-closure-prompt.md).
|
||||
// gap M-1 / acceptance D.1 (the project's SCEP gap-closure spec).
|
||||
// Build a PKIMessage encrypted to a freshly-generated RA cert whose
|
||||
// matching private key the server does NOT have. The handler MUST
|
||||
// reject (RFC 8894 path can't decrypt → falls through; MVP path can't
|
||||
|
||||
@@ -614,7 +614,7 @@ func TestSCEPIntuneEnrollment_RateLimited_E2E(t *testing.T) {
|
||||
// race with t.Parallel(), and signal.Notify is global). The SIGHUP
|
||||
// goroutine's only job is to call Reload, so calling Reload directly is
|
||||
// the equivalent contract — and stable in tests. Phase B frozen
|
||||
// decision #3 in cowork/scep-bundle-gap-closure-prompt.md.
|
||||
// decision #3 in the project's SCEP gap-closure spec.
|
||||
func TestSCEPIntuneEnrollment_TrustAnchorSIGHUPReload_E2E(t *testing.T) {
|
||||
fix := newIntuneE2EFixture(t)
|
||||
now := time.Now()
|
||||
|
||||
@@ -357,7 +357,7 @@ func (r *Router) RegisterHandlers(reg HandlerRegistry) {
|
||||
// the handler (M-003 pattern); non-admin callers get 403.
|
||||
r.Register("GET /api/v1/admin/crl/cache", http.HandlerFunc(reg.AdminCRLCache.ListCache))
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9.2 + Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md). All three endpoints are
|
||||
// (the project's SCEP GUI restructure spec). All three endpoints are
|
||||
// admin-gated at the handler layer; the M-008 regression scanner pins
|
||||
// the gate set and TestM008_AdminGatedHandlers_HaveTripletTests
|
||||
// enforces the per-handler test triplet.
|
||||
|
||||
@@ -309,7 +309,7 @@ func TestGoogleCAS_Issue_RegionalAPIUnavailable_RetryableSurface(t *testing.T) {
|
||||
// (We deliberately do NOT exercise the service-layer audit-row
|
||||
// rollback here — that's an integration test owned by
|
||||
// internal/service/revocation_svc_test.go. Mixing concerns would
|
||||
// re-introduce the exact "lying field" footgun CLAUDE.md warns
|
||||
// re-introduce the exact "lying field" footgun the project guidelines warn
|
||||
// against. The adapter contract is the single thing under test.)
|
||||
func TestGoogleCAS_Revoke_PermissionDenied_DoesNotSilentlySwallow(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// snapshot bytes to restore the previous cert. Mirrors the Bundle 5+
|
||||
// pre-deploy-snapshot + on-failure-restore pattern from IIS / WinCertStore /
|
||||
// JavaKeystore. Rank 5 of the 2026-05-03 Infisical deep-research
|
||||
// deliverable (cowork/infisical-deep-research-results.md Part 5).
|
||||
// deliverable (the project's deep-research deliverable, Part 5).
|
||||
//
|
||||
// IAM permissions required:
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package awsacm_test
|
||||
|
||||
// Rank 5 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Per-error-class
|
||||
// (the project's deep-research deliverable, Part 5). Per-error-class
|
||||
// failure tests for the AWS ACM target connector — mirrors the
|
||||
// awsacmpca_failure_test.go shape (commit 60dce0b) on the issuer side.
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package awsacm_test
|
||||
|
||||
// Rank 5 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Happy-path table-
|
||||
// (the project's deep-research deliverable, Part 5). Happy-path table-
|
||||
// driven tests for the AWS ACM target connector. Mirrors the
|
||||
// k8ssecret_test.go ergonomics + the Bundle 5+ atomic-rollback
|
||||
// assertions from IIS / WinCertStore / JavaKeystore.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// permission which we deliberately keep off the minimum-RBAC surface.
|
||||
//
|
||||
// Rank 5 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5).
|
||||
// (the project's deep-research deliverable, Part 5).
|
||||
//
|
||||
// Required Azure RBAC (minimum):
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package azurekv_test
|
||||
|
||||
// Rank 5 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Happy-path tests
|
||||
// (the project's deep-research deliverable, Part 5). Happy-path tests
|
||||
// for the Azure Key Vault target connector. Mirrors the awsacm_test.go
|
||||
// shape so cross-cloud regressions are bisectable side-by-side.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// rename-race correctness.
|
||||
//
|
||||
// All 12 are required by the prompt at
|
||||
// cowork/deploy-hardening-i-prompt.md::"Test plan (Phase 1
|
||||
// the project's deploy-hardening I spec::"Test plan (Phase 1
|
||||
// ships ≥95% coverage on the new package)".
|
||||
//
|
||||
// The tests run in non-root environments — they do NOT exercise
|
||||
|
||||
@@ -17,7 +17,7 @@ import "time"
|
||||
// durable record of who approved + why.
|
||||
//
|
||||
// Rank 7 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Closes the
|
||||
// (the project's deep-research deliverable, Part 5). Closes the
|
||||
// "two-person integrity / four-eyes principle" procurement gap for
|
||||
// PCI-DSS Level 1, FedRAMP Moderate / High, and SOC 2 Type II
|
||||
// customers.
|
||||
|
||||
@@ -125,7 +125,7 @@ type RenewalPolicy struct {
|
||||
// grow Prometheus cardinality on a typo).
|
||||
//
|
||||
// Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5).
|
||||
// (the project's deep-research deliverable, Part 5).
|
||||
AlertChannels map[string][]string `json:"alert_channels,omitempty"`
|
||||
|
||||
// AlertSeverityMap maps each threshold-day value to its severity
|
||||
|
||||
@@ -228,7 +228,7 @@ const (
|
||||
// (ACM) — the public AWS service that ALB / CloudFront / API
|
||||
// Gateway / App Runner consume by ARN. Rank 5 of the 2026-05-03
|
||||
// Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). See
|
||||
// (the project's deep-research deliverable, Part 5). See
|
||||
// docs/connectors.md "AWS Certificate Manager" section for the
|
||||
// operator playbook including minimum IAM policy + atomic-rollback
|
||||
// contract.
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// of the system control these — M-005).
|
||||
//
|
||||
// An attacker who plants "ignore previous instructions" inside any of
|
||||
// those fields can steer LLM consumers (Claude, Cursor, custom agents)
|
||||
// those fields can steer LLM consumers (any MCP-compatible AI client)
|
||||
// of the certctl MCP server. certctl's own MCP server cannot prevent
|
||||
// the LLM consumer from honoring such injection on its own — but it
|
||||
// CAN make the trust boundary explicit so consumers that fence
|
||||
|
||||
@@ -1260,7 +1260,7 @@ func registerHealthTools(s *gomcp.Server, c *Client) {
|
||||
// I-2 closure (cat-i-b0924b6675f8): pre-I-2 the README claimed "all
|
||||
// API endpoints are exposed via MCP" but the discovered-certificate
|
||||
// lifecycle (claim + dismiss) was never wrapped — operators using
|
||||
// MCP clients (Claude, Cursor, etc.) had no path to bring an
|
||||
// MCP clients had no path to bring an
|
||||
// out-of-band cert under management or to mark a benign discovery
|
||||
// as not-of-interest without dropping to the REST API directly.
|
||||
// These two tools wrap the existing HTTP handlers
|
||||
|
||||
@@ -6,7 +6,7 @@ import "testing"
|
||||
//
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 2.5: every parser certctl
|
||||
// adds gets a Fuzz target in the same package (the fuzz-target-ownership
|
||||
// rule from cowork/CLAUDE.md::Operating Rules). The point isn't to find
|
||||
// per the project's operating rules). The point isn't to find
|
||||
// vulnerabilities (the parser uses stdlib encoding/asn1 which is itself
|
||||
// fuzzed upstream) — it's to prove that arbitrary attacker-controlled
|
||||
// bytes cannot panic the SCEP server. Any panic = an availability bug.
|
||||
|
||||
@@ -1112,7 +1112,7 @@ func (s *Scheduler) runCRLGeneration(ctx context.Context) {
|
||||
var ErrSchedulerShutdownTimeout = errors.New("scheduler graceful shutdown timeout")
|
||||
|
||||
// acmeGCLoop runs every acmeGCInterval and invokes ACMEGarbageCollector.
|
||||
// Per CLAUDE.md "Scheduler idempotency" architecture decision: an
|
||||
// Per the project's scheduler-idempotency architecture decision: an
|
||||
// atomic.Bool guard prevents concurrent tick execution; the
|
||||
// sync.WaitGroup tracks the in-flight goroutine for graceful shutdown.
|
||||
// Phase 5.
|
||||
|
||||
@@ -1039,7 +1039,7 @@ type FinalizeOrderResult struct {
|
||||
//
|
||||
// The window between Step B and Step C can leave a managed_certificates
|
||||
// row whose order is still in `processing`. Phase 5's GC scheduler
|
||||
// reconciles. Documented in cowork/acme-server-prompts/03-... + the
|
||||
// reconciles. Documented in the project's ACME-server design notes + the
|
||||
// service file's design notes.
|
||||
func (s *ACMEService) FinalizeOrder(
|
||||
ctx context.Context,
|
||||
@@ -1293,7 +1293,7 @@ func randIDSuffix() string {
|
||||
// base32encode emits the lowercase Crockford-style base32 alphabet
|
||||
// without padding. Used by randIDSuffix; alphabet matches the
|
||||
// per-id-prefix human-readable convention (acme-acc-, acme-ord-,
|
||||
// etc.) — see CLAUDE.md "TEXT primary keys with human-readable
|
||||
// etc.) — see the project's "TEXT primary keys with human-readable
|
||||
// prefixes" architecture decision.
|
||||
func base32encode(b []byte) string {
|
||||
const alpha = "0123456789abcdefghjkmnpqrstvwxyz"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// ExpiryAlertMetrics is a thread-safe counter table for the per-policy
|
||||
// multi-channel expiry-alert dispatch path. Rank 4 of the 2026-05-03
|
||||
// Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Closes the
|
||||
// (the project's deep-research deliverable, Part 5). Closes the
|
||||
// procurement-checklist gap where a customer who configured PagerDuty
|
||||
// for cert-expiry pages got silent nothing — ExpirationWarning shipped
|
||||
// only to Email pre-fix.
|
||||
|
||||
@@ -324,7 +324,7 @@ func (s *RenewalService) CheckExpiringCertificates(ctx context.Context) error {
|
||||
// team get paged?".
|
||||
//
|
||||
// Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). The policy
|
||||
// (the project's deep-research deliverable, Part 5). The policy
|
||||
// argument is nil-safe — a cert with no RenewalPolicy attached gets the
|
||||
// back-compat Email-only default matrix.
|
||||
func (s *RenewalService) sendThresholdAlerts(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package service
|
||||
|
||||
// Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
// (cowork/infisical-deep-research-results.md Part 5). Pins every leg of
|
||||
// (the project's deep-research deliverable, Part 5). Pins every leg of
|
||||
// the per-policy multi-channel expiry-alert fan-out matrix:
|
||||
//
|
||||
// 1. Default matrix → Email-only at every tier (back-compat).
|
||||
|
||||
@@ -57,7 +57,7 @@ type SCEPService struct {
|
||||
|
||||
// Per-profile metadata surfaced by the new /admin/scep/profiles
|
||||
// endpoint. SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md). All fields are nil/zero
|
||||
// (the project's SCEP GUI restructure spec). All fields are nil/zero
|
||||
// when the operator runs without Intune AND without mTLS — we still
|
||||
// surface the always-present challenge-password-set + RA cert
|
||||
// expiry on the Profiles tab for those.
|
||||
@@ -292,7 +292,7 @@ func (s *SCEPService) SetMTLSConfig(enabled bool, bundlePath string) {
|
||||
// compatibility for the Phase 9 admin contract.
|
||||
//
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md).
|
||||
// (the project's SCEP GUI restructure spec).
|
||||
type SCEPProfileStatsSnapshot struct {
|
||||
// Always-present per-profile fields.
|
||||
PathID string `json:"path_id"`
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
// ErrAgentNotFound is returned by [TargetService.CreateTarget] when the caller
|
||||
// references an agent_id that is empty or does not correspond to a registered
|
||||
// agent. The handler layer maps this to HTTP 400 via [errors.Is]. See C-002 in
|
||||
// cowork/certctl-coverage-gap-audit.md — this sentinel replaces a silent
|
||||
// the project's coverage-gap audit — this sentinel replaces a silent
|
||||
// Postgres FK violation (23503 → HTTP 500) with a deterministic 400.
|
||||
var ErrAgentNotFound = errors.New("referenced agent does not exist")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-- Rank 4 of the 2026-05-03 Infisical deep-research deliverable
|
||||
-- (cowork/infisical-deep-research-results.md Part 5). Adds the
|
||||
-- (the project's deep-research deliverable, Part 5). Adds the
|
||||
-- per-policy channel matrix that the multi-channel expiry-alert
|
||||
-- routing reads from. Two JSONB columns:
|
||||
--
|
||||
@@ -16,7 +16,7 @@
|
||||
--
|
||||
-- Both columns use IF NOT EXISTS so the migration is idempotent —
|
||||
-- safe to re-run on every certctl-server boot per the
|
||||
-- "Idempotent migrations" architecture decision in CLAUDE.md.
|
||||
-- the project's "Idempotent migrations" architecture decision.
|
||||
|
||||
ALTER TABLE renewal_policies
|
||||
ADD COLUMN IF NOT EXISTS alert_channels JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- 000027_approval_workflow.up.sql
|
||||
-- Rank 7 of the 2026-05-03 Infisical deep-research deliverable
|
||||
-- (cowork/infisical-deep-research-results.md Part 5). Two-person
|
||||
-- (the project's deep-research deliverable, Part 5). Two-person
|
||||
-- integrity / four-eyes principle for compliance-tier certificate
|
||||
-- issuance. CertificateProfile.RequiresApproval gates the renewal-
|
||||
-- loop entry; issuance_approval_requests captures the per-job
|
||||
@@ -8,7 +8,7 @@
|
||||
--
|
||||
-- All operations use IF NOT EXISTS / IF EXISTS so the migration is
|
||||
-- idempotent — safe to re-run on every certctl-server boot per the
|
||||
-- "Idempotent migrations" architecture decision in CLAUDE.md.
|
||||
-- the project's "Idempotent migrations" architecture decision.
|
||||
--
|
||||
-- Existing scaffolding REUSED (not redefined here):
|
||||
-- - JobStatusAwaitingApproval enum value (internal/domain/job.go).
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
--
|
||||
-- All operations use IF NOT EXISTS / IF EXISTS so the migration is
|
||||
-- idempotent — safe to re-run on every certctl-server boot per the
|
||||
-- "Idempotent migrations" architecture decision in CLAUDE.md.
|
||||
-- the project's "Idempotent migrations" architecture decision.
|
||||
--
|
||||
-- Defense in depth: NEVER persist CA private key bytes. The
|
||||
-- key_driver_id column is a reference (filesystem path / KMS key ID
|
||||
|
||||
@@ -85,7 +85,7 @@ Current helpers:
|
||||
- **`QA-doc Part-count drift`** + **`QA-doc seed-count drift`** — these
|
||||
protect docs-the-operator-reads, not anything the product depends on.
|
||||
Moved to `make verify-docs` (operator runs pre-tag, not on every push).
|
||||
See `cowork/ci-pipeline-cleanup-prompt.md` Phase 11.
|
||||
See the ci-pipeline-cleanup spec, Phase 11.
|
||||
|
||||
## Running the full set locally
|
||||
|
||||
|
||||
Executable → Regular
+4
-4
@@ -2,12 +2,12 @@
|
||||
# scripts/ci-guards/S-1-hardcoded-source-counts.sh
|
||||
#
|
||||
# S-1 master closed cat-s1-9ce1cbe26876 (README + features.md
|
||||
# stale numeric counts; explicit CLAUDE.md violation per
|
||||
# stale numeric counts; explicit violation of project guidelines per
|
||||
# "version-stamped numbers rot") and
|
||||
# cat-s1-features_md_issuer_count_contradiction (features.md
|
||||
# self-disagreed on issuer count: 9 vs 12 in the same doc).
|
||||
# The fix replaced source-derived numbers in prose with
|
||||
# "rebuild via <command>" patterns documented in CLAUDE.md::
|
||||
# "rebuild via <command>" patterns documented in the project guidelines under
|
||||
# "Current-state commands". This script grep-fails the build if
|
||||
# any of the previously-stale sites reintroduces a hardcoded
|
||||
# count.
|
||||
@@ -35,8 +35,8 @@ if [ -n "$BAD" ]; then
|
||||
echo "::error::S-1 regression: hardcoded source-count prose reappeared:"
|
||||
echo "$BAD"
|
||||
echo ""
|
||||
echo "CLAUDE.md rule: 'Numeric claims about current state rot.'"
|
||||
echo "Replace the count with the grep command from CLAUDE.md::"
|
||||
echo "Project rule: 'Numeric claims about current state rot.'"
|
||||
echo "Replace the count with the grep command documented under"
|
||||
echo "'Current-state commands' (e.g. 'ls -d internal/connector/issuer/*/ | wc -l')"
|
||||
echo "or rephrase to reference the rebuild command on the same line."
|
||||
echo "See coverage-gap-audit-2026-04-24-v5/unified-audit.md"
|
||||
|
||||
@@ -313,7 +313,7 @@ export const reloadAdminSCEPIntuneTrust = (pathID: string) =>
|
||||
});
|
||||
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md): per-profile SCEP admin
|
||||
// (the project's SCEP GUI restructure spec): per-profile SCEP admin
|
||||
// surface backing the Profiles tab on the SCEP Administration page.
|
||||
// M-008 admin-gated; same gating semantics as the existing
|
||||
// getAdminSCEPIntuneStats helper.
|
||||
|
||||
@@ -682,7 +682,7 @@ export interface IntuneReloadTrustResponse {
|
||||
}
|
||||
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md): per-profile SCEP admin
|
||||
// (the project's SCEP GUI restructure spec): per-profile SCEP admin
|
||||
// snapshot. Backs the new /api/v1/admin/scep/profiles endpoint and
|
||||
// the Profiles tab on the SCEP Administration page.
|
||||
//
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md): Vitest coverage for the
|
||||
// (the project's SCEP GUI restructure spec): Vitest coverage for the
|
||||
// rebranded SCEP Administration page. Pins:
|
||||
// 1. Admin gate — non-admin sees the gated banner; admin requests are
|
||||
// never issued.
|
||||
|
||||
@@ -20,7 +20,7 @@ import type {
|
||||
} from '../api/types';
|
||||
|
||||
// SCEP RFC 8894 + Intune master bundle Phase 9 follow-up
|
||||
// (cowork/scep-gui-restructure-prompt.md): per-profile SCEP
|
||||
// (the project's SCEP GUI restructure spec): per-profile SCEP
|
||||
// administration page with three tabs.
|
||||
//
|
||||
// Profiles (default) — every configured SCEP profile, lean card per
|
||||
|
||||
Reference in New Issue
Block a user