Update test counts (525+ → 600+), table counts (17 → 18), endpoint counts (68 → 70), add revocation/CRL endpoints to API overview, add certificate_revocations table to schema docs, update M15 roadmap to show M15a complete and M15b remaining. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
19 KiB
certctl — Self-Hosted Certificate Lifecycle Platform
TLS certificate lifespans are shrinking. The CA/Browser Forum passed Ballot SC-081v3 unanimously in April 2025, setting a phased reduction: 200 days by March 2026, 100 days by March 2027, and 47 days by March 2029. Manual certificate management is no longer viable at any scale.
certctl is a self-hosted platform for end-to-end certificate lifecycle automation — from issuance through renewal to deployment — with zero human intervention. Track every certificate in your organization, automatically renew them before they expire, and deploy them to your servers without touching a terminal. Private keys never leave your infrastructure.
What It Does
certctl gives you a single pane of glass for every TLS certificate in your organization. The web dashboard shows your full certificate inventory — what's healthy, what's expiring, what's already expired, and who owns each one. The REST API (70 endpoints) lets you automate everything. Agents deployed on your infrastructure generate private keys locally and submit CSRs — private keys never leave your servers. The background scheduler watches expiration dates and triggers renewals automatically — when certificate lifespans drop to 47 days, certctl handles the constant rotation without human involvement.
flowchart LR
subgraph "Control Plane"
API["REST API + Dashboard\n:8443"]
PG[("PostgreSQL")]
end
subgraph "Your Infrastructure"
A1["Agent"] --> T1["NGINX"]
A2["Agent"] --> T2["Apache / HAProxy"]
A3["Agent"] --> T3["F5 · IIS"]
end
API --> PG
A1 & A2 & A3 -->|"CSR + status\n(no private keys)"| API
API -->|"Signed certs"| A1 & A2 & A3
API -->|"Issue/Renew"| CA["Certificate Authorities\nLocal CA · ACME"]
Screenshots
Quick Start
Docker Compose (Recommended)
git clone https://github.com/shankar0123/certctl.git
cd certctl
docker compose -f deploy/docker-compose.yml up -d --build
Wait ~30 seconds, then open http://localhost:8443 in your browser.
The dashboard comes pre-loaded with 15 demo certificates, 5 agents, policy rules, audit events, and notifications — a realistic snapshot of a certificate inventory so you can explore immediately.
Verify the API:
curl http://localhost:8443/health
# {"status":"healthy"}
curl -s http://localhost:8443/api/v1/certificates | jq '.total'
# 15
Manual Build
# Prerequisites: Go 1.22+, PostgreSQL 16+
go mod download
make build
# Set up database
export CERTCTL_DATABASE_URL="postgres://certctl:certctl@localhost:5432/certctl?sslmode=disable"
export CERTCTL_AUTH_TYPE=none
make migrate-up
# Start server
./bin/server
# Start agent (separate terminal)
export CERTCTL_SERVER_URL=http://localhost:8443
export CERTCTL_API_KEY=change-me-in-production
export CERTCTL_AGENT_NAME=local-agent
export CERTCTL_AGENT_ID=agent-local-01
./bin/agent --agent-id=agent-local-01
Documentation
| Guide | Description |
|---|---|
| Concepts | TLS certificates explained from scratch — for beginners who know nothing about certs |
| Quick Start | Get running in 5 minutes with accurate API examples |
| Demo Walkthrough | 5-7 minute guided stakeholder presentation |
| Advanced Demo | Issue a certificate end-to-end with technical deep-dives |
| Architecture | System design, data flow diagrams, security model |
| Connectors | Build custom issuer, target, and notifier connectors |
Architecture
flowchart TB
subgraph "Control Plane (certctl-server)"
DASH["Web Dashboard\nReact SPA"]
API["REST API\nGo 1.22 net/http"]
SVC["Service Layer"]
REPO["Repository Layer\ndatabase/sql + lib/pq"]
SCHED["Scheduler\nRenewal · Jobs · Health · Notifications"]
end
subgraph "Data Store"
PG[("PostgreSQL 16\n18 tables\nTEXT primary keys")]
end
subgraph "Agents"
AG["certctl-agent\nKey generation · CSR · Deployment"]
end
DASH --> API
API --> SVC --> REPO --> PG
SCHED --> SVC
AG -->|"Heartbeat + CSR"| API
API -->|"Cert + Chain"| AG
Key Design Decisions
- Private keys isolated from the control plane. Agents generate ECDSA P-256 keys locally and submit CSRs (public key only). The server signs the CSR and returns the certificate — private keys never touch the control plane. Server-side keygen is available via
CERTCTL_KEYGEN_MODE=serverfor demo/development only. - TEXT primary keys, not UUIDs. IDs are human-readable prefixed strings (
mc-api-prod,t-platform,o-alice) so you can identify resource types at a glance in logs and queries. - Handler → Service → Repository layering. Handlers define their own service interfaces for clean dependency inversion. No global service singletons.
- Idempotent migrations. All schema uses
IF NOT EXISTSand seed data usesON CONFLICT (id) DO NOTHING, safe for repeated execution.
Database Schema
| Table | Purpose |
|---|---|
managed_certificates |
Certificate records with metadata, status, expiry, tags |
certificate_versions |
Historical versions with PEM chains and CSRs |
renewal_policies |
Renewal window, auto-renew settings, retry config, alert thresholds |
issuers |
CA configurations (Local CA, ACME, etc.) |
deployment_targets |
Target systems (NGINX, F5, IIS) with agent assignments |
agents |
Registered agents with heartbeat tracking, OS/arch/IP metadata |
jobs |
Issuance, renewal, deployment, and validation jobs |
teams |
Organizational groups for certificate ownership |
owners |
Individual owners with email for notifications |
policy_rules |
Enforcement rules (allowed issuers, environments, metadata) |
policy_violations |
Flagged non-compliance with severity levels |
audit_events |
Immutable action log (append-only, no update/delete) |
notification_events |
Email and webhook notification records |
certificate_target_mappings |
Many-to-many cert ↔ target relationships |
certificate_profiles |
Named enrollment profiles with allowed key types, max TTL, crypto constraints |
agent_groups |
Dynamic device grouping by OS, architecture, IP CIDR, version |
agent_group_members |
Manual include/exclude membership for agent groups |
certificate_revocations |
Revocation records with RFC 5280 reason codes, serial numbers, issuer notification status |
Configuration
All server environment variables use the CERTCTL_ prefix:
| Variable | Default | Description |
|---|---|---|
CERTCTL_SERVER_HOST |
127.0.0.1 |
Server bind address |
CERTCTL_SERVER_PORT |
8080 |
Server listen port |
CERTCTL_DATABASE_URL |
postgres://localhost/certctl |
PostgreSQL connection string |
CERTCTL_DATABASE_MAX_CONNS |
25 |
Connection pool size |
CERTCTL_LOG_LEVEL |
info |
Log level: debug, info, warn, error |
CERTCTL_LOG_FORMAT |
json |
Log format: json or text |
CERTCTL_AUTH_TYPE |
api-key |
Auth mode: api-key, jwt, or none |
CERTCTL_AUTH_SECRET |
— | Required for api-key and jwt auth types |
CERTCTL_KEYGEN_MODE |
agent |
Key generation mode: agent (production) or server (demo only) |
CERTCTL_ACME_DIRECTORY_URL |
— | ACME directory URL (e.g., Let's Encrypt staging) |
CERTCTL_ACME_EMAIL |
— | Contact email for ACME account registration |
CERTCTL_ACME_CHALLENGE_TYPE |
— | ACME challenge type: http-01 (default) or dns-01 |
CERTCTL_CA_CERT_PATH |
— | Path to CA certificate for sub-CA mode |
CERTCTL_CA_KEY_PATH |
— | Path to CA private key for sub-CA mode |
CERTCTL_STEPCA_URL |
— | step-ca server URL |
CERTCTL_STEPCA_PROVISIONER |
— | step-ca JWK provisioner name |
Agent environment variables:
| Variable | Default | Description |
|---|---|---|
CERTCTL_SERVER_URL |
http://localhost:8080 |
Control plane URL |
CERTCTL_API_KEY |
— | Agent API key |
CERTCTL_AGENT_NAME |
certctl-agent |
Agent display name |
CERTCTL_AGENT_ID |
— | Registered agent ID (required) |
CERTCTL_KEY_DIR |
/var/lib/certctl/keys |
Directory for storing private keys (agent keygen mode) |
Docker Compose overrides these for the demo stack (see deploy/docker-compose.yml): port 8443, auth type none, database pointing to the postgres container.
API Overview
All endpoints are under /api/v1/ and return JSON. List endpoints support pagination (?page=1&per_page=50).
Certificates
GET /api/v1/certificates List (filter: status, environment, owner_id, team_id)
POST /api/v1/certificates Create
GET /api/v1/certificates/{id} Get
PUT /api/v1/certificates/{id} Update
DELETE /api/v1/certificates/{id} Archive (soft delete)
GET /api/v1/certificates/{id}/versions Version history
POST /api/v1/certificates/{id}/renew Trigger renewal → 202 Accepted
POST /api/v1/certificates/{id}/deploy Trigger deployment → 202 Accepted
POST /api/v1/certificates/{id}/revoke Revoke with RFC 5280 reason code
GET /api/v1/crl Certificate Revocation List (JSON)
Agents
GET /api/v1/agents List
POST /api/v1/agents Register
GET /api/v1/agents/{id} Get
POST /api/v1/agents/{id}/heartbeat Record heartbeat
POST /api/v1/agents/{id}/csr Submit CSR for issuance
GET /api/v1/agents/{id}/certificates/{certId} Retrieve signed certificate
GET /api/v1/agents/{id}/work Poll for pending deployment jobs
POST /api/v1/agents/{id}/jobs/{jobId}/status Report job completion/failure
Infrastructure
GET /api/v1/issuers List issuers
POST /api/v1/issuers Create
GET /api/v1/issuers/{id} Get
PUT /api/v1/issuers/{id} Update
DELETE /api/v1/issuers/{id} Delete
POST /api/v1/issuers/{id}/test Test connectivity
GET /api/v1/targets List deployment targets
POST /api/v1/targets Create
GET /api/v1/targets/{id} Get
PUT /api/v1/targets/{id} Update
DELETE /api/v1/targets/{id} Delete
Organization
GET /api/v1/teams List teams
POST /api/v1/teams Create
GET /api/v1/teams/{id} Get
PUT /api/v1/teams/{id} Update
DELETE /api/v1/teams/{id} Delete
GET /api/v1/owners List owners
POST /api/v1/owners Create
GET /api/v1/owners/{id} Get
PUT /api/v1/owners/{id} Update
DELETE /api/v1/owners/{id} Delete
Operations
GET /api/v1/jobs List (filter: status, type)
GET /api/v1/jobs/{id} Get
POST /api/v1/jobs/{id}/cancel Cancel
GET /api/v1/policies List policy rules
POST /api/v1/policies Create
PUT /api/v1/policies/{id} Update (enable/disable)
DELETE /api/v1/policies/{id} Delete
GET /api/v1/policies/{id}/violations List violations for rule
GET /api/v1/audit Query audit trail
GET /api/v1/notifications List notifications
POST /api/v1/notifications/{id}/read Mark as read
Auth
GET /api/v1/auth/info Auth mode info (no auth required)
GET /api/v1/auth/check Validate credentials
Health
GET /health Server health check
GET /ready Readiness check
Supported Integrations
Certificate Issuers
| Issuer | Status | Type |
|---|---|---|
| Local CA (self-signed + sub-CA) | Implemented | GenericCA |
| ACME v2 (Let's Encrypt, Sectigo) | Implemented (HTTP-01 + DNS-01) | ACME |
| step-ca | Implemented | StepCA |
| OpenSSL / Custom CA | Planned (V2) | — |
| Vault PKI | Planned | — |
| DigiCert | Planned | — |
Note: ADCS integration is handled via the Local CA's sub-CA mode — certctl operates as a subordinate CA with its signing certificate issued by ADCS.
Deployment Targets
| Target | Status | Type |
|---|---|---|
| NGINX | Implemented | NGINX |
| Apache httpd | Implemented | Apache |
| HAProxy | Implemented | HAProxy |
| F5 BIG-IP | Interface only (V2) | F5 |
| Microsoft IIS | Interface only (V2) | IIS |
| Kubernetes Secrets | Planned | — |
Notifiers
| Notifier | Status | Type |
|---|---|---|
| Email (SMTP) | Implemented | Email |
| Webhooks | Implemented | Webhook |
| Slack | Planned | — |
Development
# Install dev tools (golangci-lint, migrate CLI, air)
make install-tools
# Run tests
make test
# Run with coverage
make test-coverage
# Lint
make lint
# Format
make fmt
Docker Compose
make docker-up # Start stack (server + postgres + agent)
make docker-down # Stop stack
make docker-logs-server # Server logs
make docker-logs-agent # Agent logs
make docker-clean # Stop + remove volumes
Security
Private Key Management
- Agent keygen mode (default): Agents generate ECDSA P-256 keys locally and store them with 0600 permissions in
CERTCTL_KEY_DIR(default/var/lib/certctl/keys). Only the CSR (public key) is sent to the control plane. Private keys never leave agent infrastructure. - Server keygen mode (demo only): Set
CERTCTL_KEYGEN_MODE=serverfor development/demo with Local CA. The control plane generates RSA-2048 keys server-side. A log warning is emitted at startup.
Authentication
- Agent-to-server: API key (registered at agent creation)
- API key and JWT auth types supported;
nonefor demo/development - Auth type and secret configured via
CERTCTL_AUTH_TYPEandCERTCTL_AUTH_SECRET
Audit Trail
- Immutable append-only log in PostgreSQL (
audit_eventstable) - Every action attributed to an actor with timestamp and resource reference
- No update or delete operations on audit records
Roadmap
V1 (v1.0.0 released)
All nine development milestones (M1–M9) are complete. The backend covers the full certificate lifecycle: Local CA and ACME v2 issuers, NGINX/Apache/HAProxy/F5/IIS target connectors, threshold-based expiration alerting, agent-side ECDSA P-256 key generation, API auth with rate limiting, and a React dashboard with 16 pages wired to the real API. The CI pipeline runs build, vet, test with coverage gates (service layer 30%+, handler layer 50%+), frontend type checking, Vitest test suite, and Vite production build on every push. 600+ tests total: 465 Go test functions + 138 subtests (207 service, 226 handler, integration with 40+ subtests, 23 connector) plus 53 frontend Vitest tests covering API client functions and utility helpers. Docker images are published to GitHub Container Registry on every version tag via the release workflow.
V2: Operational Maturity
- M10: Agent Metadata + Targets ✅ — agents report OS, architecture, IP, hostname, version via heartbeat; Apache httpd and HAProxy target connectors
- M11: Crypto Policy + Profiles + Ownership ✅ — certificate profiles (named enrollment profiles with allowed key types, max TTL, crypto constraints), certificate ownership tracking (owners + teams + notification routing), dynamic agent groups (OS/arch/IP CIDR/version matching), interactive renewal approval (AwaitingApproval state)
- M12: Sub-CA + DNS-01 + step-ca ✅ — Local CA sub-CA mode (enterprise root chain with RSA/ECDSA/PKCS#8), ACME DNS-01 challenges (script-based DNS hooks for any provider, wildcard cert support), step-ca issuer connector (native /sign API with JWK provisioner auth)
- M13: GUI Operations — bulk cert operations (renew, revoke, reassign), deployment timeline, inline policy editor, target config wizard, audit export, short-lived credentials dashboard
- M14: Enterprise Connectors — SSE/WebSocket real-time updates, F5 BIG-IP, IIS, ADCS, OpenSSL/Custom CA implementations
- M15a: Core Revocation ✅ — revocation API with all RFC 5280 reason codes, JSON CRL endpoint, webhook + email revocation notifications, best-effort issuer notification,
certificate_revocationstable with idempotent recording, 48 new tests - M15b: OCSP + Bulk Revocation + GUI — embedded OCSP responder, DER-encoded X.509 CRL, short-lived cert exemption, bulk revocation by profile/owner/agent, revocation GUI
- M16: Team Adoption — OIDC/SSO, RBAC (profile-gated), CLI tool, Slack/Teams/PagerDuty/OpsGenie notifiers, bulk cert import
- M17: Observability — expiration calendar, health scores, compliance scoring, Prometheus metrics (issuance/revocation rates, OCSP latency), deployment rollback
- M18: Integrations — MCP server (OpenClaw/Claude/Cursor), CT Log monitoring, DigiCert issuer, filesystem cert discovery
V3: Discovery, Visibility & Cloud
Discovery engine (passive/active scanning, cert chain validation, unknown cert detection, triage workflows), Kubernetes cert-manager external issuer, cloud targets (AWS ALB/IAM Roles Anywhere, Azure Key Vault/Managed Identity, Palo Alto, FortiGate, Citrix ADC, Kubernetes Secrets), extended issuers (Entrust, GlobalSign, Google CAS, EJBCA, Vault PKI), ServiceNow integration, Ansible module
V4+: Platform & Scale
Kubernetes CRD, Terraform provider, multi-region, HA control plane, HSM support, LDAP auth, API key scoping, multi-tenancy, SPIFFE/SPIRE federation, OPA policy backend, compliance reporting (NIST, SOC 2, PCI-DSS)
License
Certctl is licensed under the Business Source License 1.1. The source code is publicly available and free to use, modify, and self-host. The one restriction: you may not offer certctl as a managed/hosted certificate management service to third parties.








