This commit is contained in:
shankar0123
2026-05-05 18:18:29 +00:00
parent 7c5cc57d75
commit 75097909e9
71 changed files with 95 additions and 771 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 | ☐ |
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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
```
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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).
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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).
+1 -1
View File
@@ -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.
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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?
+1 -1
View File
@@ -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").
+1 -1
View File
@@ -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`.