go-acme/lego
Let's Encrypt/ACME client and library written in Go
Healthy across all four use cases
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 today
- ✓11 active contributors
- ✓MIT licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Single-maintainer risk — top contributor 90% of recent commits
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/go-acme/lego)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/go-acme/lego on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: go-acme/lego
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/go-acme/lego 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 all four use cases
- Last commit today
- 11 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Single-maintainer risk — top contributor 90% of recent commits
<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 go-acme/lego
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/go-acme/lego.
What it runs against: a local clone of go-acme/lego — 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 go-acme/lego | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | 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 ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of go-acme/lego. If you don't
# have one yet, run these first:
#
# git clone https://github.com/go-acme/lego.git
# cd lego
#
# 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 go-acme/lego and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "go-acme/lego(\\.git)?\\b" \\
&& ok "origin remote is go-acme/lego" \\
|| miss "origin remote is not go-acme/lego (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT 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 "acme/api/api.go" \\
&& ok "acme/api/api.go" \\
|| miss "missing critical file: acme/api/api.go"
test -f "acme/api/order.go" \\
&& ok "acme/api/order.go" \\
|| miss "missing critical file: acme/api/order.go"
test -f "challenge/resolver/solver_manager.go" \\
&& ok "challenge/resolver/solver_manager.go" \\
|| miss "missing critical file: challenge/resolver/solver_manager.go"
test -f "challenge/dns01/dns_challenge.go" \\
&& ok "challenge/dns01/dns_challenge.go" \\
|| miss "missing critical file: challenge/dns01/dns_challenge.go"
test -f "certificate/certificates.go" \\
&& ok "certificate/certificates.go" \\
|| miss "missing critical file: certificate/certificates.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 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
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/go-acme/lego"
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
lego is a complete ACME (Automated Certificate Management Environment) client and library written in Go that automates certificate lifecycle management with Let's Encrypt and other ACME-compliant CAs. It implements RFC 8555 (ACME v2) with extensions for TLS-ALPN challenges, IP address certificates, and renewal information (ARI), and ships with ~180 DNS provider integrations for DNS-01 challenge automation. Monorepo structured as: acme/api/ contains the core ACME protocol implementation (account.go, order.go, challenge.go, certificate.go) with internal utilities for JWS signing, nonce management, and HTTP requests; providers/ (inferred from DNS provider count) contains challenge solvers; cmd/ holds the CLI; certcrypto/ provides cryptographic utilities. The acme/api/internal/ subdirectory isolates nonce management, secure signing, and HTTP sender logic.
👥Who it's for
DevOps engineers, system administrators, and Go developers who need to programmatically obtain, renew, and revoke TLS certificates at scale—either via the CLI tool (./cmd) or by embedding the library (github.com/go-acme/lego/v4) into their Go applications.
🌱Maturity & risk
Production-ready and actively maintained. The project has substantial test coverage (test files throughout acme/api/, certcrypto/), comprehensive CI/CD via GitHub Actions (.github/workflows/), Docker image support, and regular releases managed by goreleaser (.goreleaser.yml). The ~180 DNS providers indicate wide ecosystem adoption.
Low risk for core functionality, but DNS provider quality varies—new providers are contributed regularly (see .github/ISSUE_TEMPLATE/new_dns_provider.yml). Dependency on external DNS APIs means transient failures are possible. Single-maintainer appearance is mitigated by active GitHub workflows and a structured contribution template, but check INCIDENT_RESPONSE_PLAN.md for incident history.
Active areas of work
Active development on ACME protocol compliance: the codebase supports multiple recent RFCs (8737 for TLS-ALPN, 8738 for IP certs, 9773 for renewal info), and draft-ietf-acme-profiles for profiles extension. The GitHub Actions workflows (pr.yml, release.yml, go-cross.yml) indicate continuous integration and multi-platform releases are ongoing.
🚀Get running
Clone and build: git clone https://github.com/go-acme/lego.git && cd lego && make build (Makefile is present). For library use: go get github.com/go-acme/lego/v4 in your Go project. For CLI: the build output will be in cmd/ or the Makefile target location.
Daily commands:
Development: make build compiles the CLI. The Makefile (present in repo root) likely has targets for test, lint, and docker. For the library: import github.com/go-acme/lego/v4, instantiate a client, and call methods on acme/api.Client (see acme/api/service.go).
🗺️Map of the codebase
acme/api/api.go— Core ACME API client initialization and orchestration—every integration begins hereacme/api/order.go— Order lifecycle management (create, finalize, download cert)—implements RFC 8555 core state machinechallenge/resolver/solver_manager.go— Challenge solver coordination across dns01, http01, tlsalpn01—central dispatch for all validation methodschallenge/dns01/dns_challenge.go— DNS-01 challenge implementation with nameserver lookups and CNAME support—most common challenge typecertificate/certificates.go— Certificate request/renewal workflow orchestration—entry point for end-user cert operationsacme/api/internal/secure/jws.go— JWS signing and cryptographic operations—handles all ACME protocol authenticationcmd/cmd.go— CLI command framework and global configuration—bridges library to executable
🛠️How to make changes
Add a new DNS provider (custom challenge solver)
- Implement the provider.ChallengeProvider interface in a new file under challenge/dns01 or external package (
challenge/provider.go) - Register the provider in solver_manager by adding a case for your provider type (
challenge/resolver/solver_manager.go) - If adding DNS record validation, extend challenge/dns01/domain.go to map your provider's nameservers (
challenge/dns01/domain.go) - Add CLI flag handling in cmd/cmd.go for your provider's credentials/config (
cmd/cmd.go)
Add a new ACME resource endpoint (e.g., new RFC extension)
- Define the resource struct in acme/commons.go following existing patterns (Account, Order, etc) (
acme/commons.go) - Create a new file acme/api/newresource.go with API methods (Create, Status, etc) that use service.Do() (
acme/api/service.go) - Add resource endpoint constants and integration tests in acme/api/newresource_test.go (
acme/api/order_test.go) - Wire the new methods into the main Client in acme/api/api.go (
acme/api/api.go)
Add certificate storage backend (e.g., database instead of files)
- Create a new implementation matching cmd/certs_storage.go interface (
cmd/certs_storage.go) - Implement Save(certPath, cert, key) and GetOlderThan(days) methods for renewal tracking (
cmd/cmd_renew.go) - Register the backend in cmd/cmd.go configuration initialization (
cmd/cmd.go) - Add CLI flags for backend selection and connection details (
cmd/cmd.go)
Add custom challenge type (beyond dns01/http01/tlsalpn01)
- Create acme/commons.go struct for your challenge type (e.g., CustomChallenge) (
acme/commons.go) - Implement challenge.Solver interface in a new challenge/customtype/solver.go (
challenge/provider.go) - Add solver instantiation logic to challenge/resolver/solver_manager.go (
challenge/resolver/solver_manager.go) - Update acme/api/challenge.go to parse your challenge type in authorization decomposition (
acme/api/challenge.go)
🔧Why these technologies
- Go (1.20+) — Cross-platform CLI and library, static binaries, minimal runtime overhead for ACME operations
- RFC 8555 ACME v2 — Industry standard for automated certificate provisioning; used by Let's Encrypt
- File-based storage (JSON) — Simplicity for CLI deployments; accounts and certs stored as plain JSON/PEM on disk
- DNS TXT queries and nameserver discovery — Most common challenge type; supports delegation via CNAME for outsourcing DNS validation
- HTTP-01 and TLS- — undefined
🪤Traps & gotchas
ACME CA endpoints vary in nonce handling and request signing expectations—tests mock these but real CAs may behave differently (see acme/api/internal/sender/ for request lifecycle). DNS provider credential management is provider-specific; no unified secret store in the library. The CLI likely requires environment variables for DNS API keys—check cmd/ and contributing docs for examples. Cross-platform compilation is tested (go-cross.yml workflow) but Docker image build path differs from local build (Dockerfile vs buildx.Dockerfile).
🏗️Architecture
💡Concepts to learn
- ACME (Automated Certificate Management Environment) — The entire library implements this protocol (RFC 8555); understanding the order-challenge-validation-issuance flow is essential to understanding lego's design
- JWS (JSON Web Signature) — ACME requires all requests be signed with JWS; lego's acme/api/internal/secure/jws.go is critical and non-trivial (key ID handling, nonce binding)
- DNS Challenge (dns-01) — One of three challenge types lego supports and by far the most automated; 180 DNS provider integrations exist because dns-01 is the most scalable method
- TLS-ALPN Challenge (tls-alpn-01) — Modern challenge type (RFC 8737) that lego implements; solves for IP addresses and avoids DNS/HTTP external dependencies
- Certificate Renewal Information (ARI) — New RFC 9773 extension lego implements; allows CAs to signal renewal timing, critical for large-scale certificate lifecycle automation
- Nonce Management — ACME uses nonces to prevent replay attacks; lego's acme/api/internal/nonces/nonce_manager.go must coordinate with CA nonce issuance patterns which vary by implementation
- Certificate Bundling — lego implements proper CA chain construction (mentioned in README); certcrypto/ handles this and it's non-obvious (intermediate vs root selection)
🔗Related repos
certbot/certbot— Python ACME client by EFF; lego's spiritual equivalent but in Go with more DNS provider integrationscaddy/caddy— Web server that uses lego (or similar) as a library for automatic HTTPS provisioning; primary real-world consumer of ACME clientstraefik/traefik— Reverse proxy that integrates ACME clients for automatic certificate provisioning; another major lego use casego-acme/lego-examples— Companion repo with runnable examples of lego library usage (if it exists; worth checking for sample code)smallstep/certificates— Alternative ACME CA written in Go; represents another ACME implementation that lego clients interact with
🪄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 DNS provider implementations
The repo contains multiple DNS provider integrations (evident from challenge/dns01 structure) but lacks visible integration test coverage for provider-specific implementations. New contributors could add integration tests for DNS providers to ensure they work correctly with real DNS APIs, reducing maintenance burden and improving reliability for users relying on specific providers.
- [ ] Review existing DNS provider implementations in challenge/dns01/ directory
- [ ] Create integration test framework in challenge/dns01/providers_integration_test.go
- [ ] Add tests for common DNS provider operations (Create, CleanUp, Present) using sandbox/mock environments
- [ ] Document test setup requirements and how to add new provider integration tests
Add unit tests for certificate renewal edge cases in certificate/renewal.go
The certificate/renewal.go file exists but renewal_test.go may lack coverage for edge cases like expired certificates, renewal timing edge cases, and domain validation scenarios. Given that renewal is a core feature, comprehensive tests would prevent regressions and help new contributors understand renewal logic.
- [ ] Audit certificate/renewal_test.go for coverage gaps using
go test -cover - [ ] Add tests for edge cases: pre-expiration renewal windows, domain changes, certificate chain validation
- [ ] Add tests for error scenarios: expired certificates, authorization failures, nonce reuse
- [ ] Document renewal test patterns in CONTRIBUTING.md for future DNS provider tests
Create GitHub Action workflow for DNS provider validation in pull requests
The repo has workflows for documentation, cross-compilation, and release (.github/workflows/) but no automated validation workflow for new DNS provider contributions. A new workflow could validate that new providers follow the interface contract and pass basic linting, reducing review burden for maintainers handling new DNS provider PRs (referenced in ISSUE_TEMPLATE/new_dns_provider.yml).
- [ ] Create .github/workflows/dns-provider-validation.yml workflow file
- [ ] Add checks: verify provider implements required interfaces from challenge/dns01/dns_challenge.go
- [ ] Add golangci-lint checks specific to provider code (checking error handling, documentation)
- [ ] Add validation that provider tests exist and pass
go test - [ ] Document in CONTRIBUTING.md how the validation workflow helps reviewer DNS provider PRs
🌿Good first issues
- Add missing test coverage for acme/api/renewal.go (Renewal Information RFC 9773 support) and acme/api/identifier_test.go gaps—both files exist but test files are sparse
- Improve error messages in acme/errors.go by mapping ACME error types to user-friendly guidance; currently no error wrapping visible
- Add integration tests or examples in cmd/ for certificate renewal workflows (the renewal.go API exists but may lack documented CLI usage)
⭐Top contributors
Click to expand
Top contributors
- @ldez — 90 commits
- @FrozenAlex — 1 commits
- @exsesa — 1 commits
- @Xiol — 1 commits
- @grigassukys — 1 commits
📝Recent commits
Click to expand
Recent commits
1da5445— chore: update linter (#3037) (ldez)fad056f— chore: improve release workflow (#3030) (ldez)12fcdd2— chore: improve release workflow (#3028) (ldez)670eb27— Detach v4.35.2 (ldez)537f2ed— Prepare release v4.35.2 (ldez)523ec03— chore: update dependencies (#3021) (ldez)a1b9413— rfc2136: fix zone env var parsing (#3020) (ldez)a50aba8— Detach v4.35.1 (ldez)4642ef3— Prepare release v4.35.1 (ldez)daff76f— Detach v4.35.0 (ldez)
🔒Security observations
The lego project demonstrates a reasonable security posture for an ACME client library. The codebase uses Go, which provides memory safety benefits. However, there are areas for improvement: (1) encrypted credentials should not be stored in version control at all, (2) Docker images should use pinned versions and non-root users, (3) CI/CD security scanning should be enhanced, and (4) security processes should be more formally documented and enforced. The project would benefit from implementing automated dependency vulnerability scanning and container image scanning in the release workflow. No SQL injection, XSS, or obvious hardcoded secrets in plaintext were detected in the visible file structure.
- Medium · Encrypted Credentials File Present in Repository —
.gitcookies.enc. The file '.gitcookies.enc' suggests encrypted credentials are being tracked in version control. While encrypted, this represents a security risk if encryption keys are compromised or if the encryption method is weak. Credentials should not be stored in repositories, even in encrypted form. Fix: Remove '.gitcookies.enc' from version control and use environment variables or secure credential management systems (e.g., GitHub Secrets, HashiCorp Vault) for authentication tokens and API credentials. - Low · Docker Base Image Uses Latest Tag —
Dockerfile. The Dockerfile uses 'golang:1-alpine' and 'alpine:3' as base images with broad version ranges. This can lead to non-deterministic builds and potential security issues if a vulnerable version is pulled during build time. Fix: Pin specific Alpine and Go versions (e.g., 'golang:1.20-alpine3.18', 'alpine:3.18'). This ensures reproducible builds and gives explicit control over vulnerability management. - Low · Missing Security Headers in Docker Configuration —
Dockerfile. The Dockerfile does not include security best practices like running as a non-root user or setting resource limits. The container may run with unnecessary privileges. Fix: Add 'USER' directive to run as non-root user, implement resource limits, and consider using multi-stage builds with minimal final image sizes. Example: 'RUN addgroup -g 1000 lego && adduser -u 1000 -G lego -s /sbin/nologin -D lego' followed by 'USER lego'. - Low · No Build-Time Security Scanning —
.github/workflows/. The GitHub Actions workflows and build configuration do not appear to include dependency vulnerability scanning, SAST analysis, or image scanning steps visible in the provided configuration snippets. Fix: Integrate security scanning tools in CI/CD pipeline: Use 'go mod tidy' with vulnerability checking, add Trivy for container image scanning, implement SAST tools like gosec, and use dependency-check or similar tools for vulnerability detection. - Low · Incident Response Plan Exists But Not Enforced —
.github/INCIDENT_RESPONSE_PLAN.md. While an INCIDENT_RESPONSE_PLAN.md exists, there is no visible enforcement mechanism or security policy documentation that would ensure its adoption across the development team. Fix: Ensure the incident response plan is documented in CONTRIBUTING.md and referenced in security policy. Add GitHub branch protection rules requiring security reviews and automated security checks before merge.
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.