Shankar 89c567b6af Rewrite README and .env.example to match actual implementation
README.md:
- Replace ASCII architecture diagram with Mermaid
- Fix all database table names (managed_certificates, audit_events, etc.)
- Fix env var names to use CERTCTL_ prefix matching config.go
- Fix API endpoint paths ({id} not :id, /audit not /audit/logs)
- Add all missing endpoints (renew, deploy, CSR, heartbeat, policies, notifications)
- Add dashboard as primary feature (was completely missing)
- Link to all new docs (concepts, advanced demo, architecture, connectors)
- Fix integration status (Local CA implemented, ACME in progress)
- Fix security section (API key auth, not mTLS)
- Remove broken links to non-existent docs (api.md, k8s-deployment.md, scaling.md)
- Remove placeholder Support & Community section

.env.example:
- Change all var names to CERTCTL_ prefix (CERTCTL_DATABASE_URL, etc.)
- Remove vars that don't exist in config.go (ACME_*, SMTP_*, feature flags)
- Add scheduler tuning vars as commented examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:01:34 -04:00
2026-03-14 20:01:53 -04:00
2026-03-14 20:01:53 -04:00
2026-03-14 20:01:53 -04:00
2026-03-14 20:01:53 -04:00
2026-03-14 20:01:53 -04:00
2026-03-14 20:01:53 -04:00

certctl — Open-Source Certificate Control Plane

A self-hosted certificate lifecycle platform. Track, renew, and deploy TLS certificates across your infrastructure with a web dashboard, REST API, and agent-based architecture where private keys never leave your servers.

License Go Report Card Status: Active Development

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 (50+ endpoints) lets you automate everything. Agents deployed on your infrastructure handle key generation and certificate deployment without exposing private keys to the control plane.

flowchart LR
    subgraph "Control Plane"
        API["REST API + Dashboard\n:8443"]
        PG[("PostgreSQL")]
    end

    subgraph "Your Infrastructure"
        A1["Agent"] --> T1["NGINX"]
        A2["Agent"] --> T2["F5 BIG-IP"]
        A3["Agent"] --> T3["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"]

Quick Start

git clone https://github.com/shankar0123/certctl.git
cd certctl
docker compose -f deploy/docker-compose.yml up -d

Wait ~30 seconds, then open http://localhost:8443 in your browser.

The dashboard comes pre-loaded with 14 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'
# 14

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
./bin/agent

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\n14 tables · TEXT 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 never touch the control plane. Agents generate keys locally and submit CSRs (public key only). The control plane forwards CSRs to the CA and returns signed certificates. Even if the control plane database is compromised, no private keys are exposed.
  • 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 EXISTS and seed data uses ON 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
issuers CA configurations (Local CA, ACME, etc.)
deployment_targets Target systems (NGINX, F5, IIS) with agent assignments
agents Registered agents with heartbeat tracking
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

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

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

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

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

Infrastructure

GET    /api/v1/issuers                    List issuers
POST   /api/v1/issuers                    Create
GET    /api/v1/issuers/{id}               Get
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

Organization

GET    /api/v1/teams                      List teams
POST   /api/v1/teams                      Create
GET    /api/v1/owners                     List owners
POST   /api/v1/owners                     Create

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
GET    /api/v1/policies/{id}/violations   List violations for rule

GET    /api/v1/audit                      Query audit trail
GET    /api/v1/notifications              List notifications

Health

GET    /health                            Server health check
GET    /ready                             Readiness check

Supported Integrations

Certificate Issuers

Issuer Status Type
Local CA (self-signed) Implemented GenericCA
ACME v2 (Let's Encrypt, Sectigo) In progress ACME
Vault PKI Planned
DigiCert Planned

Deployment Targets

Target Status Type
NGINX Implemented NGINX
F5 BIG-IP Implemented F5
Microsoft IIS Implemented 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

  • Private keys are generated exclusively on agents, never sent to the control plane
  • Keys stored with file permissions 0600
  • Old keys deleted after successful certificate renewal

Authentication

  • Agent-to-server: API key (registered at agent creation)
  • API key and JWT auth types supported; none for demo/development
  • Auth type and secret configured via CERTCTL_AUTH_TYPE and CERTCTL_AUTH_SECRET

Audit Trail

  • Immutable append-only log in PostgreSQL (audit_events table)
  • Every action attributed to an actor with timestamp and resource reference
  • No update or delete operations on audit records

Roadmap

Summary:

  • V1 (current): Dashboard, inventory, alerting, Local CA + ACME issuers, NGINX/F5/IIS targets, agents, REST API, policies, audit trail, Docker Compose
  • V2: Charts/trends, bulk import, OIDC/SSO, deployment rollback, CLI, Slack/Teams
  • V3: Certificate discovery, network scanning, unknown cert detection
  • V4+: Kubernetes CRD, Terraform provider, multi-region, HA control plane, HSM support

License

Certctl is licensed under the Apache License 2.0. See LICENSE file for details.

S
Description
certctl is a self-hosted platform that automates the entire certificate lifecycle — from issuance through renewal to deployment — with zero human intervention. It works with any certificate authority, deploys to any server, and keeps private keys on your infrastructure where they belong.
Readme 47 MiB
Languages
Go 77.2%
TypeScript 19.2%
Shell 2.2%
PLpgSQL 0.7%
JavaScript 0.3%
Other 0.3%