smallstep/certificates
🛡️ A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH.
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2d ago
- ✓8 active contributors
- ✓Distributed ownership (top contributor 38% of recent commits)
Show all 6 evidence items →Show less
- ✓Apache-2.0 licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/smallstep/certificates)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/smallstep/certificates on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: smallstep/certificates
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/smallstep/certificates shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 2d ago
- 8 active contributors
- Distributed ownership (top contributor 38% of recent commits)
- Apache-2.0 licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live smallstep/certificates
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/smallstep/certificates.
What it runs against: a local clone of smallstep/certificates — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in smallstep/certificates | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 32 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of smallstep/certificates. If you don't
# have one yet, run these first:
#
# git clone https://github.com/smallstep/certificates.git
# cd certificates
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of smallstep/certificates and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "smallstep/certificates(\\.git)?\\b" \\
&& ok "origin remote is smallstep/certificates" \\
|| miss "origin remote is not smallstep/certificates (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "authority/authority.go" \\
&& ok "authority/authority.go" \\
|| miss "missing critical file: authority/authority.go"
test -f "acme/db/nosql/nosql.go" \\
&& ok "acme/db/nosql/nosql.go" \\
|| miss "missing critical file: acme/db/nosql/nosql.go"
test -f "acme/api/handler.go" \\
&& ok "acme/api/handler.go" \\
|| miss "missing critical file: acme/api/handler.go"
test -f "api/api.go" \\
&& ok "api/api.go" \\
|| miss "missing critical file: api/api.go"
test -f "authority/admin/api/admin.go" \\
&& ok "authority/admin/api/admin.go" \\
|| miss "missing critical file: authority/admin/api/admin.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/smallstep/certificates"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
step-ca is a self-hosted, online certificate authority (CA) that issues X.509 and SSH certificates via ACME, enabling automated certificate management for DevOps infrastructure. It includes built-in support for ACME challenge types (HTTP-01, DNS-01, TLS-ALPN-01), SSH certificate issuance tied to identity tokens or cloud instance identity, and can be deployed as a lightweight alternative to Let's Encrypt or complex enterprise PKI systems. Modular monorepo organized by protocol and feature: acme/ contains ACME account, challenge, authorization, and certificate logic; acme/api/ contains HTTP handlers; acme/db/nosql/ abstracts persistence; authority/ (inferred) handles CA core logic; pki/ likely manages X.509 generation. Supporting packages handle Vault KMS integration, SSH provisioners, and identity provider (OIDC, cloud) bindings. The entry point is likely in cmd/ (not shown) that wires these modules into a running server.
👥Who it's for
DevOps engineers, infrastructure teams, and Kubernetes operators who need to automate TLS certificate provisioning for internal services, VMs, containers, and SSH access without managing a heavyweight enterprise CA. Also useful for developers building ACME-integrated systems who want a self-hosted, fully controllable certificate authority.
🌱Maturity & risk
Production-ready. The repository shows active maintenance (4.3MB of Go code, comprehensive test coverage visible in *_test.go files across all modules), robust CI/CD (GitHub Actions workflows in .github/workflows/), and semantic versioning via .VERSION and .goreleaser.yml. The codebase spans X.509/SSH certificate handling, ACME protocol compliance (RFC 8555), and multiple storage backends (Badger, Vault, Google Cloud KMS), indicating battle-tested maturity.
Low-to-moderate risk. The project has a deep dependency tree (Google Cloud, Vault, OIDC, TPM libraries) requiring careful updates, but dependencies are actively maintained (Go 1.25.0, recent versions of go-jose, go-tpm, linkedca). Single organization (smallstep) maintains it, so roadmap depends on their commercial product priorities. Breaking changes are tracked in CHANGELOG.md, but the API surface is broad (ACME, SSH, X.509) so upgrades warrant careful testing.
Active areas of work
Active development on ACME protocol completeness, TPM/attestation support (go-attestation, challenge_tpmsimulator_test.go), and identity provider integrations (coreos/go-oidc, Vault auth backends). Recent additions include cloud.google.com/go/security for Google Cloud integration and fxamacker/cbor for serialization. Dependabot is configured, indicating continuous dependency updates.
🚀Get running
Clone the repository and build using Make: git clone https://github.com/smallstep/certificates.git && cd certificates && make build. Examine the Makefile for test and lint targets. The .VERSION file and .goreleaser.yml show the release process. Review CONTRIBUTING.md for development guidelines and the README for configuration and deployment instructions.
Daily commands:
make build compiles the binary (output likely to bin/ or bin/step-ca based on Go conventions). Run ./bin/step-ca with a valid configuration file (see CONTRIBUTING.md or README for provisioning examples). The Makefile and .github/workflows/ci.yml show test/lint: make test, make lint. Docker image available (Dockerfile present in root).
🗺️Map of the codebase
authority/authority.go— Core certificate authority implementation—central to all signing and validation logic that every CA feature depends on.acme/db/nosql/nosql.go— ACME database abstraction layer handling persistent state for accounts, orders, challenges—required reading for understanding data flow.acme/api/handler.go— ACME protocol HTTP handler routing all certificate requests—essential for understanding the request/response pipeline.api/api.go— Main CA REST API router and middleware setup—entry point for non-ACME certificate operations.authority/admin/api/admin.go— Admin API for provisioner and policy management—critical for understanding authorization and configuration flows.acme/challenge.go— Challenge validation logic (HTTP-01, DNS-01, TLS-ALPN) used across all ACME issuance workflows.Makefile— Build configuration and test targets—defines how the codebase is compiled and validated.
🛠️How to make changes
Add a new ACME API endpoint
- Define the ACME request/response types in a new method in acme/api/ (
acme/api/handler.go) - Implement the handler function following the pattern in acme/api/account.go or acme/api/order.go (
acme/api/account.go) - Register the route in the ACME router within acme/api/handler.go's registerHandlers method (
acme/api/handler.go) - Add database methods in acme/db/nosql/ if new persistent state is needed (
acme/db/nosql/nosql.go) - Write integration tests in acme/api/_test.go and acme/db/nosql/_test.go (
acme/api/handler_test.go)
Add a new certificate signing policy
- Define policy logic as a method in the Authority struct in authority/authority.go (
authority/authority.go) - Integrate policy check into the sign endpoint validation in api/sign.go (
api/sign.go) - Add provisioner configuration option in authority/provisioner.go (if separate provisioner types needed) (
authority/admin/api/admin.go) - Write unit tests in authority/authority_test.go and api/sign_test.go (
api/sign_test.go)
Add a new admin API endpoint
- Create the handler function in authority/admin/api/ (e.g., admin.go or acme.go) (
authority/admin/api/admin.go) - Define request/response types and validation logic (
authority/admin/api/admin.go) - Register the route in the admin API router (
authority/admin/api/admin.go) - Write tests in authority/admin/api/*_test.go (
authority/admin/api/admin_test.go)
Add support for a new key type or signature algorithm
- Extend certificate generation logic in acme/certificate.go for ACME issuance (
acme/certificate.go) - Update signing validation in api/sign.go to accept the new algorithm (
api/sign.go) - Add SSH key support in api/ssh.go if SSH certificates should support it (
api/ssh.go) - Write integration tests validating the new algorithm end-to-end (
api/sign_test.go)
🔧Why these technologies
- Go — Compiled, fast, strong stdlib for crypto/TLS; ideal for a high-performance PKI service.
- BadgerDB — Embedded key-value store for ACME state without external DB dependencies; suitable for small-to-medium deployments.
- go-chi/v5 — Lightweight HTTP router with composable middleware for ACME and admin API routing.
- go-jose/v3 — JOSE/JWT library supporting JWS verification required by ACME protocol.
- Hashicorp Vault — Optional integration for secure private key storage and HSM support in production deployments.
⚖️Trade-offs already made
-
Embedded BadgerDB instead of external PostgreSQL/MySQL
- Why: Simpler deployment without external dependencies; easier for small teams and edge deployments.
- Consequence: Limited horizontal scaling; single-machine deployments; more complex HA setups required.
-
RFC 8555 ACME implementation over proprietary protocol
- Why: Industry-standard allows integration with existing ACME clients (certbot, lego, etc.); ecosystem compatibility.
- Consequence: Bound to ACME spec constraints; features outside ACME require alternative APIs (e.g., admin API).
-
In-memory nonce tracking for ACME replay protection
- Why: Simple, fast; no external service needed.
- Consequence: Nonces lost on restart; distributed CA deployments need custom nonce store implementation.
-
Provisioner-based policy engine vs. declarative policy language
- Why: Go code provisioners allow flexible, powerful policies without new DSL.
- Consequence: Policy changes require code recompilation; not ideal for config-driven use cases.
🚫Non-goals (don't propose these)
- Does not provide built-in multi-node clustering or HA replication out-of-box.
- Does not implement certificate transparency (CT) logging by default.
- Does not manage client identity beyond basic TLS/JWS authentication.
- Does not provide a web UI for certificate management (REST API only).
🪤Traps & gotchas
- ACME account creation requires proper JWS (JSON Web Signature) formatting via go-jose/v3; malformed requests fail silently. 2. Challenge validation callbacks (HTTP-01, DNS-01, TLS-ALPN-01) must be externally reachable or mocked in tests; local testing requires configuration. 3. Badger key-value store (used by default) requires careful transaction handling; see acme/db/nosql/ for proper patterns. 4. SSH certificate provisioning depends on identity provider configuration (OIDC issuer URL, keys); missing setup breaks SSH issuance without clear error messages. 5. TPM simulator tests (challenge_tpmsimulator_test.go) require google/go-tpm and specific platform support; may fail on unsupported architectures.
🏗️Architecture
💡Concepts to learn
- ACME (Automated Certificate Management Environment) — step-ca's primary protocol; understanding RFC 8555 is essential for navigating the acme/ package structure and API contract
- JWS (JSON Web Signature) — ACME clients must sign all requests with JWS; step-ca uses go-jose/v3 to verify signatures in middleware.go; cryptographic validation failure is a common debugging pain point
- X.509 Certificates and Subject Alternative Names (SANs) — step-ca's core output format; domain binding, certificate constraints, and issuance policies all revolve around SAN extension validation
- Key Derivation and Storage Backends (BadgerDB, Vault KMS) — acme/db/nosql/ abstracts persistence; understanding the storage layer is critical for scaling, backup, and integrating external KMS
- SSH Certificates (RFC 4253/6187) — step-ca issues SSH user and host certificates tied to identity attestation; the SSH provisioner logic differs significantly from X.509 issuance
- Nonce Management and Replay Protection — ACME requires nonce tracking to prevent replay attacks; middleware.go implements nonce validation; misconfiguration here introduces security vulnerabilities
- TPM 2.0 Attestation and Device Identity — step-ca integrates google/go-tpm for hardware-backed certificate enrollment; challenge_tpmsimulator_test.go shows the verification flow for attestation-based provisioning
🔗Related repos
smallstep/cli— Command-line client companion tool that integrates with step-ca for certificate enrollment, renewal, and SSH key management; users of certificates typically use both togetherletsencrypt/boulder— Let's Encrypt's open-source ACME server implementation; a reference architecture for large-scale ACME deployments and protocol compliance patternshashicorp/vault— Complementary secret management and KMS platform that step-ca integrates with (hashicorp/vault/api in go.mod) for key storage and rotationsmallstep/linkedca— Smallstep's web-based admin console for step-ca; manages CA configuration, policies, and issuance metrics (imported as smallstep/linkedca in dependencies)
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for ACME EAB (External Account Binding) workflow
The ACME EAB implementation exists across acme/api/eab.go, acme/api/eab_test.go, and acme/db/nosql/eab.go, but there's no wire integration test similar to acme/api/wire_integration_test.go that tests the full EAB flow (account creation with EAB, order placement, challenge completion). This is critical for a CA system where EAB is often required for enterprise deployments and bugs here could compromise security.
- [ ] Create acme/api/eab_wire_integration_test.go with end-to-end EAB account provisioning tests
- [ ] Test account creation with valid/invalid EAB credentials against the full API stack
- [ ] Test order placement and authorization with EAB-bound accounts
- [ ] Verify EAB nonce reuse prevention and account reuse across EAB bindings
- [ ] Reference existing wire_integration_test.go patterns for consistency
Add unit test coverage for ACME Challenge TPM simulator edge cases
The file acme/challenge_tpmsimulator_test.go exists but is minimal. Given that go.step.sm/crypto v0.77.9 and github.com/google/go-tpm v0.9.8 are core dependencies and TPM attestation is increasingly important for zero-trust infrastructure, comprehensive TPM challenge validation tests are critical. The challenge_test.go file likely has generic challenge tests, but TPM-specific scenarios need dedicated coverage.
- [ ] Expand acme/challenge_tpmsimulator_test.go with tests for malformed TPM attestations
- [ ] Add tests for TPM credential ID validation and replay attack prevention
- [ ] Test TPM challenge state transitions (pending → valid → invalid)
- [ ] Add tests for attestation certificate chain validation failures
- [ ] Test interaction between TPM challenges and acme/challenge_wire_test.go scenarios
Add missing database abstraction tests for NoSQL operations across all entity types
The acme/db/nosql/ directory has separate test files for account, authz, certificate, challenge, eab, nonce, order, and wire, but there's no acme/db/nosql/integration_test.go that validates cross-entity transactions (e.g., order → authz → challenge → certificate lifecycle, cascading deletes, consistency). This is critical for data integrity in a CA system that handles concurrent requests.
- [ ] Create acme/db/nosql/integration_test.go with multi-entity transaction tests
- [ ] Test complete order lifecycle: account → order → authorizations → challenges → certificate
- [ ] Test concurrent modifications to the same order/authorization and verify atomicity
- [ ] Test data consistency when operations fail mid-transaction (e.g., certificate generation fails after challenge validated)
- [ ] Add tests for proper cleanup of expired nonces, orders, and authorizations
🌿Good first issues
- Add missing test coverage for acme/account.go error paths: the file has no corresponding error case tests for account validation failures, lockout, or malformed input. Implement table-driven tests in acme/account_test.go covering rejected account creation scenarios.
- Document ACME challenge type mappings: the codebase supports HTTP-01, DNS-01, and TLS-ALPN-01 (mentioned in README) but acme/challenge.go lacks inline comments explaining the validation lifecycle for each type. Add examples to CONTRIBUTING.md showing how to test each challenge locally.
- Improve error messages in acme/api/handler.go: several handlers return generic HTTP error responses without context (e.g., malformed JWS in middleware.go). Add structured logging (using logrus) to log the actual validation failure reason before returning 400/403, helping operators debug client misconfigurations.
⭐Top contributors
Click to expand
Top contributors
- @step-ci — 38 commits
- @dependabot[bot] — 36 commits
- @maraino — 9 commits
- @dopey — 9 commits
- @tashian — 3 commits
📝Recent commits
Click to expand
Recent commits
10075d9— Merge pull request #2671 from smallstep/dependabot/go_modules/google.golang.org/grpc-1.81.0 (step-ci)af4834c— Bump google.golang.org/grpc from 1.80.0 to 1.81.0 (dependabot[bot])d62dd59— Merge pull request #2670 from smallstep/dependabot/go_modules/cloud.google.com/go/security-1.23.0 (step-ci)d5bb2a2— Bump cloud.google.com/go/security from 1.22.0 to 1.23.0 (dependabot[bot])d7e8403— Merge pull request #2669 from smallstep/dependabot/go_modules/cloud.google.com/go/longrunning-0.12.0 (step-ci)46a1b34— Merge pull request #2667 from smallstep/dependabot/go_modules/github.com/fxamacker/cbor/v2-2.9.2 (step-ci)1b8201e— Bump github.com/fxamacker/cbor/v2 from 2.9.1 to 2.9.2 (dependabot[bot])13e645b— Merge pull request #2672 from smallstep/mariano/fix-linter (maraino)be2523c— Fix linter errors (maraino)bc3cef4— Bump cloud.google.com/go/longrunning from 0.11.0 to 0.12.0 (dependabot[bot])
🔒Security observations
- High · Outdated Go Version —
go.mod (go 1.25.0). The project specifies Go 1.25.0, which appears to be a future/unrealistic version number. This may indicate a configuration error or the use of an unstable/non-existent Go release. If this is actually an older version (e.g., 1.21.0), it may contain unpatched security vulnerabilities. Fix: Verify the actual Go version being used. Ensure it matches a stable release and is kept up-to-date with security patches. Update to the latest stable Go version (currently 1.23.x or later). - High · Deprecated Dependency: dgraph-io/badger v1 —
go.mod - github.com/dgraph-io/badger v1.6.2. The project includes github.com/dgraph-io/badger v1.6.2, which is an outdated major version. Badger v1 is no longer actively maintained and may contain unpatched security vulnerabilities. The codebase also includes v2, suggesting incomplete migration. Fix: Migrate fully to dgraph-io/badger v2 or v3 (latest stable). Remove the v1 dependency to avoid security risks from unmaintained code. - High · Potential Cryptographic Implementation Risks —
acme/, api/, and crypto-related modules. The codebase handles X.509 certificates, SSH keys, and ACME protocol which are cryptographically sensitive. Dependencies like golang.org/x/crypto v0.50.0 may be outdated relative to current standards, and custom cryptographic code in acme/ and api/ modules could contain implementation flaws. Fix: Conduct a thorough security audit of all cryptographic implementations. Keep golang.org/x/crypto and go.step.sm/crypto updated to latest versions. Consider third-party cryptographic code review. - Medium · Incomplete Dependency Lock File —
go.mod - truncated indirect dependencies. The go.mod file appears truncated ('da' at the end of indirect dependencies). This suggests the dependency list is incomplete, making it impossible to verify all transitive dependencies for vulnerabilities. Fix: Ensure go.mod is complete and run 'go mod tidy' to regenerate a clean dependency tree. Verify the file is not corrupted or incomplete. - Medium · Database Security - Multiple Database Backends —
acme/db/nosql/, acme/db/ modules. The codebase supports multiple database backends (badger v1, v2, nosql). This complexity increases attack surface. The presence of both v1 and v2 of badger suggests potential for using the insecure v1 in production. Fix: Standardize on a single, well-maintained database backend. Remove support for deprecated versions. Conduct security testing of database interaction code, particularly around data serialization and access control. - Medium · ACME Implementation Complexity —
acme/ module (multiple files). The ACME server implementation is extensive with multiple components (challenges, authorizations, orders, accounts). Complex protocol implementations are prone to security flaws, including authorization bypasses and validation issues. Fix: Conduct security-focused code review of ACME implementation. Test edge cases in challenge validation, authorization logic, and order state transitions. Consider fuzzing the ACME API endpoints. - Medium · Certificate Revocation Logic Security —
api/crl.go, acme/api/revoke.go. CRL generation and revocation handling are critical security components. The api/crl.go module handles revocation, which if implemented incorrectly could allow revoked certificates to be accepted. Fix: Perform thorough security testing of revocation logic. Ensure revoked certificates are properly tracked and rejected. Test OCSP responder behavior if applicable. - Low · Dependency on External OIDC Provider —
go.mod - github.com/coreos/go-oidc/v3 v3.18.0. The coreos/go-oidc/v3 dependency indicates OIDC authentication is implemented. Misconfiguration of OIDC can lead to authentication bypass. Fix: Verify OIDC configuration is properly validated.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.