FiloSottile/mkcert
A simple zero-config tool to make locally trusted development certificates with any names you'd like.
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.
- ✓29+ active contributors
- ✓BSD-3-Clause licensed
- ✓CI configured
Show all 6 evidence items →Show less
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠Concentrated ownership — top contributor handles 67% 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/filosottile/mkcert)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/filosottile/mkcert on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: FiloSottile/mkcert
Generated by RepoPilot · 2026-05-07 · 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/FiloSottile/mkcert 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
- 29+ active contributors
- BSD-3-Clause licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Concentrated ownership — top contributor handles 67% 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 FiloSottile/mkcert
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/FiloSottile/mkcert.
What it runs against: a local clone of FiloSottile/mkcert — 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 FiloSottile/mkcert | Confirms the artifact applies here, not a fork |
| 2 | License is still BSD-3-Clause | 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 ≤ 662 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of FiloSottile/mkcert. If you don't
# have one yet, run these first:
#
# git clone https://github.com/FiloSottile/mkcert.git
# cd mkcert
#
# 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 FiloSottile/mkcert and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "FiloSottile/mkcert(\\.git)?\\b" \\
&& ok "origin remote is FiloSottile/mkcert" \\
|| miss "origin remote is not FiloSottile/mkcert (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
&& ok "license is BSD-3-Clause" \\
|| miss "license drift — was BSD-3-Clause 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 "main.go" \\
&& ok "main.go" \\
|| miss "missing critical file: main.go"
test -f "cert.go" \\
&& ok "cert.go" \\
|| miss "missing critical file: cert.go"
test -f "truststore_darwin.go" \\
&& ok "truststore_darwin.go" \\
|| miss "missing critical file: truststore_darwin.go"
test -f "truststore_linux.go" \\
&& ok "truststore_linux.go" \\
|| miss "missing critical file: truststore_linux.go"
test -f "truststore_windows.go" \\
&& ok "truststore_windows.go" \\
|| miss "missing critical file: truststore_windows.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 662 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~632d)"
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/FiloSottile/mkcert"
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
mkcert is a Go CLI tool that automates creation and installation of locally-trusted development certificates without configuration. It generates a local Certificate Authority (CA), installs it into your OS and browser trust stores (macOS Keychain, Windows CertStore, Firefox NSS, Java truststore, Linux CA bundle), and issues certificates for any hostname you specify—solving the self-signed cert warning problem for local development on domains like localhost, 127.0.0.1, example.test, and custom names. Monolithic single-package design: main.go handles CLI argument parsing and orchestration; cert.go contains certificate generation logic using crypto/x509 and crypto/tls; platform-specific truststore logic split into truststore_darwin.go (macOS Keychain), truststore_windows.go (CertStore), truststore_linux.go (system CA bundle), truststore_nss.go (Firefox NSS), and truststore_java.go (Java cacerts). Minimal code organization—everything is in the repo root.
👥Who it's for
Full-stack developers and DevOps engineers building local development environments who need HTTPS without certificate warnings. Specifically: those using Docker/Kubernetes locally, testing HTTPS-only APIs, developing against third-party integrations that require valid certs, or running local testing infrastructure that needs to trust custom domains.
🌱Maturity & risk
Production-ready and actively maintained. The project has substantial GitHub stars, uses GitHub Actions for CI/CD (see .github/workflows/test.yml and release.yml), requires Go 1.13+ for builds, and is written in pure Go with minimal dependencies (4 total: golang.org/x/net, golang.org/x/crypto, howett.net/plist, software.sslmate.com/src/go-pkcs12). The single-file architecture (main.go, cert.go, truststore_*.go) suggests it's stable and not undergoing major architectural changes.
Low dependency risk: only 4 direct deps, all well-maintained packages from trusted sources. Single maintainer (FiloSottile) creates operational risk, but the tool is narrowly scoped to local development only—the rootCA-key.pem file is generated locally and never transmitted, so compromise is local-only. The rootCA-key.pem security warning in README indicates the maintainer is security-conscious. Main risk: if not updated for new Go versions or new OS/browser trust store APIs, it could break on future system updates.
Active areas of work
No specific PR or milestone data visible in provided files, but the GitHub workflows (test.yml, release.yml) indicate active CI/CD automation. The go.mod requires Go 1.18+ and pins specific patch versions of dependencies, suggesting regular maintenance. The README installation instructions span multiple package managers (Homebrew, apt, yum, pacman, zypper, Chocolatey, Scoop) indicating broad platform support is actively maintained.
🚀Get running
Clone, build, and test with:
git clone https://github.com/FiloSottile/mkcert && cd mkcert
go build -ldflags "-X main.Version=$(git describe --tags)"
./mkcert -help
Or install pre-built binary via Homebrew: brew install mkcert (macOS) or download from releases.
Daily commands: No dev server—mkcert is a single-shot CLI tool. Run it directly:
./mkcert -install # Install local CA into trust stores
./mkcert example.com localhost 127.0.0.1 ::1 # Generate cert+key
Requires administrative/sudo access for truststore modification on Linux/Windows.
🗺️Map of the codebase
main.go— Entry point handling CLI commands (-install, -uninstall, certificate creation) and OS-specific trust store integration logiccert.go— Core certificate generation logic using x509, implements CA creation and signed certificate issuancetruststore_darwin.go— macOS-specific trust store integration; critical for ~25% of development environmentstruststore_linux.go— Linux trust store integration including NSS, Java keystore, and system CA bundle pathstruststore_windows.go— Windows certificate store integration via system APIs; handles HKLM registry and CertAddCertificateContextToStoretruststore_nss.go— NSS database handling for Firefox and Chrome on Linux; orchestrates certutil tool interactionsgo.mod— Declares Go 1.18 minimum and x509/crypto dependencies essential for certificate operations
🧩Components & responsibilities
- main.go CLI dispatcher (flag stdlib, runtime.GOOS, os.Getenv) — Parse -install/-uninstall/-help flags, orchestrate CA/cert creation, call trust store functions
- Failure mode: Invalid flags, missing args → shows usage; CA creation fails → cannot proceed
- cert.go certificate engine (crypto/x509, crypto/rsa, crypto/sha256, encoding/pem) — Generate CA RSA key pairs, create self-signed x509 CA cert, sign leaf certificates with SAN
- Failure mode: Key generation fails (rare) → panic; cert signing fails → return error to main; file write fails → error logged
- truststore_darwin.go macOS integration (os/exec, security CLI, plist parsing) — Add CA to Keychain via security CLI; locate and update Firefox profile trust settings
- Failure mode: security CLI not found → install fails; Keychain locked → cannot add; user cancels prompt → install aborted
- truststore_linux.go Linux integration (os/exec, file I/O, certutil CLI) — Copy CA to /etc/ssl/certs, run update-ca-certificates; populate Java cacerts path, NSS db path discovery
- Failure mode: Insufficient privileges → cannot write /etc/ssl; update-ca-certificates not found → CA not trusted system-wide
- truststore_windows.go Windows integration (golang.org/x/sys/windows, WinCrypt syscalls) — Call WinCrypt APIs to add cert to CurrentUser\Root trust store
- Failure mode: User denies cert prompt → cert not added; API call fails → error returned; registry locked → cannot persist
- truststore_nss.go Firefox/Chrome integration (os/exec, certutil CLI, home directory enumeration) — Locate NSS database, invoke certutil to add CA to ~/.mozilla/firefox and ~/.config/google-chrome profiles
- Failure mode: certutil not installed → NSS certs not added; db locked by running browser → certutil fails; profile not found → silently skipped
- truststore_java.go JDK integration (os/exec, keytool CLI) — Locate keytool, add CA to cacerts truststore with default password
- Failure mode: keytool not found → Java certs not installed; cacerts file locked → keytool import fails; password changed → cannot add
🛠️How to make changes
Add support for a new trust store (e.g., new Linux distro)
- Create new file truststore_<platform>.go with installCert() and uninstallCert() functions matching the signature in truststore_darwin.go (
truststore_<platform>.go) - Import and conditionally call your functions in main.go based on runtime.GOOS (
main.go) - Add test cases in .github/workflows/test.yml to verify on the target platform (
.github/workflows/test.yml)
Modify certificate generation parameters (validity period, key size, extensions)
- Edit the x509.Certificate struct initialization and key generation logic (likely in the cert creation function) (
cert.go) - Update any hardcoded paths or expiry constants (
cert.go)
Add a new CLI flag (e.g., -days, -rsa-bits)
- Parse new flag using flag package and pass to certificate generation function (
main.go) - Thread the parameter through to cert.go functions that need it (
cert.go)
🔧Why these technologies
- Go 1.18 — Compiled single-binary distribution, cross-platform support without runtime dependencies, crypto/x509 stdlib
- crypto/x509 and crypto/rsa — Native Go certificate generation without external CA tools (OpenSSL, etc.)
- Platform-specific syscalls and CLIs (security, certutil, keytool) — Only way to persistently install certificates in each OS's native trust store; requires shell execution
- github.com/howett.net/plist — Parse macOS Keychain plist data structures for trust settings
- software.sslmate.com/go-pkcs12 — Generate PKCS#12 archives for Java and Windows import scenarios
⚖️Trade-offs already made
-
Single monolithic main.go with platform #ifdefs instead of plugin architecture
- Why: Simplicity, zero-config goal, avoids runtime module loading complexity
- Consequence: Binary size slightly larger; new OS support requires code changes not extensions
-
Shell-execute external tools (certutil, keytool, security) instead of direct OS APIs
- Why: Portability, users likely have these already installed, avoids CGO complexity
- Consequence: Fragility if tools not in PATH; slower than direct syscalls; harder to debug tool failures
-
Store CA in ~/.local/share/mkcert/ (XDG) with no encryption
- Why: Zero-config, expected for local dev tool, encryption overhead for single-machine trust
- Consequence: CA key vulnerable to local filesystem compromise; not production-grade
-
Hard-coded 2048-bit RSA, 825-day validity for certs
- Why: Secure by default without CLI tuning, aligns with browser cert validity rules
- Consequence: Cannot issue longer-lived certs or support EC/Ed25519 without code change
🚫Non-goals (don't propose these)
- Does not manage private CAs across multiple machines or teams (single-machine trust store only)
- Does not support remote/cloud certificate signing or online revocation checking
- Does not provide certificate renewal automation; user must re-run mkcert to update certs
- Not compatible with Windows older than Windows 7 or Linux distros without standard CA bundle paths
- Does not encrypt or password-protect the local CA key
🪤Traps & gotchas
Critical: mkcert requires write access to system trust stores (/etc/ssl/certs on Linux, Keychain on macOS, Registry on Windows)—sudo/admin privs needed. Firefox/NSS certs install only if certutil is in PATH (installable via libnss3-tools/nss-tools). Java cert installation only works if JAVA_HOME is set or java is in PATH. On Linux, systemd-resolved may cache DNSSEC validation differently than system DNS; test URLs with actual DNS lookups. The rootCA-key.pem is stored in the user's home directory mkcert config folder (platform-dependent: ~/.config/mkcert on Linux, ~/Library/Application Support/mkcert on macOS); if lost, certs become untrusted and you must regenerate.
🏗️Architecture
💡Concepts to learn
- X.509 Certificate — mkcert generates X.509 certs using crypto/x509; understanding cert structure (subject, issuer, extensions, SAN) is critical for modifying cert generation in cert.go.
- Certificate Authority (CA) and Root of Trust — mkcert's core pattern: generate a local CA, install it as a trusted root, then issue certs signed by that CA; understanding CA hierarchy and trust chain is essential.
- Subject Alternative Name (SAN) — mkcert accepts multiple domain names and IP addresses as arguments; these are added to cert's SAN extension, allowing a single cert to serve multiple hosts.
- PKCS#12 Keystore Format — mkcert exports certs via software.sslmate.com/src/go-pkcs12 library; understanding PKCS#12 encoding is needed for cert export and Java keystore integration.
- OS Trust Store APIs — mkcert's main complexity: integrating with macOS Keychain, Windows CertStore, Linux CA bundles, Firefox NSS, and Java cacerts—each has different CLI/API surface.
- Platform-Specific Code Compilation — mkcert uses Go's build constraints (e.g., //go:build darwin) to conditionally compile truststore_*.go files; essential for cross-platform Go projects.
- Self-Signed Certificate — mkcert solves the self-signed cert problem by pre-installing its CA root, eliminating browser warnings—understanding the difference between self-signed and CA-signed certs is foundational.
🔗Related repos
denji/homebrew-nginx— Not directly related, but represents local development tooling ecosystem mkcert users depend on.certbot/certbot— Production-focused Let's Encrypt client; mkcert is its local development counterpart for zero-config local CA instead of public CA.smallstep/cli— Enterprise certificate generation tool with more features; mkcert is simpler, zero-config alternative for local dev.Homebrew/homebrew-core— mkcert is packaged and distributed here; key downstream for macOS users.golang/crypto— mkcert depends on golang.org/x/crypto and golang.org/x/net; understanding Go's crypto libraries is essential for contributing certificate logic.
🪄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 platform-specific integration tests for truststore_*.go files
The repo has 5 truststore implementation files (darwin, java, linux, nss, windows) but the test.yml workflow doesn't show platform-specific testing. New contributors could add integration tests that verify certificate installation actually works in the system trust stores on each OS. This is critical since mkcert's core value is seamless trust store integration, yet there's no evidence these codepaths are tested in CI.
- [ ] Create truststore_darwin_test.go to test certificate installation in macOS Keychain
- [ ] Create truststore_linux_test.go to test NSS and system CA bundle integration
- [ ] Create truststore_windows_test.go to test Windows Certificate Store integration
- [ ] Update .github/workflows/test.yml to run platform-specific tests on matrix OS runners (ubuntu, macos, windows)
- [ ] Add test certificates that verify uninstall operations work correctly
Add support for custom certificate output formats in cert.go
Currently mkcert outputs .pem files and -pkcs12 format for Java, but enterprise/integration use cases often need PKCS#8 encrypted keys, DER format, or other formats. The cert.go file is the core certificate generation logic but lacks flexible output serialization. This would be a clean API addition: accept a -format flag and implement format-specific writers.
- [ ] Add flag parsing in main.go for -format parameter (pem, der, pkcs8, pkcs1)
- [ ] Refactor cert.go write operations into format-specific functions (writePEM, writeDER, writePKCS8, writePKCS12)
- [ ] Use software.sslmate.com/src/go-pkcs12 and golang.org/x/crypto/pkcs8 for serialization
- [ ] Add tests in cert.go for each output format with openssl verification
- [ ] Update README.md with -format examples
Implement cross-platform uninstall functionality with -uninstall flag
The repo has -install flag but no corresponding -uninstall. The truststore_*.go files implement InstallCert but most don't have clean uninstall mechanisms. This is valuable for CI/CD, testing, and developer cleanup workflows. A new contributor could add uninstall implementations matching each platform's existing install pattern.
- [ ] Add UninstallCert() method signature to all truststore_*.go files matching their platform APIs
- [ ] Implement truststore_darwin.go UninstallCert using Security framework to remove from Keychain
- [ ] Implement truststore_linux.go UninstallCert for system CA bundle and NSS database removal
- [ ] Implement truststore_windows.go UninstallCert using Windows cert store APIs
- [ ] Add main.go -uninstall flag that calls the platform-specific UninstallCert method
- [ ] Add integration tests verifying certificate is actually removed from trust stores
🌿Good first issues
- Add test coverage for truststore_java.go—currently no test file exists; write tests that mock keytool CLI invocation and verify cacerts keystore modification without requiring actual Java installation.
- Document and implement support for detecting and installing to alternative NSS locations (Thunderbird, Evolution, Chromium snap on Linux)—truststore_nss.go only handles Firefox currently; research standard NSS database paths per Linux distro and add helpers.
- Add -uninstall flag for certificate cleanup—main.go has install logic but no corresponding uninstall function to remove the CA from all truststore locations; implement platform-specific removal mirroring truststore_*.go installation functions.
⭐Top contributors
Click to expand
Top contributors
- @FiloSottile — 67 commits
- @adamdecaf — 4 commits
- @dennisameling — 2 commits
- @arp242 — 2 commits
- @JohanGallardo — 1 commits
📝Recent commits
Click to expand
Recent commits
1c1dc4e— README: fixed broken readme link to arch linux package (#580) (JohanGallardo)2a46726— Drop "go vet" from CI due to false positives (FiloSottile)7abefc9— Add dl.filippo.io instructions to the README (FiloSottile)d7ab78d— Only print help when called without arguments (FiloSottile)abde85b— Correct failed to save CA certificate message (#399) (tan9)e4df8ab— Print the right hosts when a CSR doesn't have SANs (FiloSottile)789f1b1— Add support for Firefox in a Snap for Ubuntu 22.04 (FiloSottile)255b830— Add Windows arm64 build to CI pipeline (#396) (dennisameling)bf27d05— Build binaries for darwin/arm64 (#433) (shadyvb)4b065ac— Update dependencies (FiloSottile)
🔒Security observations
The mkcert tool demonstrates reasonable security practices as a certificate management utility, but has significant concerns: (1) severely outdated cryptographic dependencies from March-April 2022 that likely contain known vulnerabilities, (2) potential issues with private key storage permissions that cannot be verified from static analysis, (3) extensive system trust store modifications that require careful validation, and (4) lack of documented security policy. The core functionality of managing local development certificates is inherently lower-risk than production CAs, but the dependency versions must be updated immediately. Code review of cert.go and truststore implementations is strongly recommended to verify proper cryptographic practices and file permission handling.
- High · Outdated Go Dependencies with Known Vulnerabilities —
go.mod and go.sum. The project uses golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 and golang.org/x/net v0.0.0-20220421235706-1d1ef9303861, which are from March-April 2022. These versions are significantly outdated and likely contain known security vulnerabilities. Critical security patches have been released since then for cryptographic operations and network handling. Fix: Update all dependencies to their latest versions: run 'go get -u' and 'go mod tidy'. Regularly update dependencies and consider using automated dependency scanning tools like Dependabot or Snyk. - Medium · Local CA Private Key Storage Security —
cert.go, truststore_*.go files. The tool creates and stores a local Certificate Authority private key on the system. The static analysis cannot verify if proper file permissions (e.g., 0600) are enforced when storing the CA private key, which could expose it to unauthorized access if stored with incorrect permissions. Fix: Ensure CA private keys are written with restrictive permissions (0600 on Unix). Verify in cert.go that os.OpenFile() or similar operations explicitly set secure file mode flags. - Medium · Potential Certificate Generation Security Issues —
cert.go. The tool generates locally-trusted certificates. Without reviewing the actual cert.go implementation, there's a risk of weak random number generation, improper certificate parameters, or insufficient entropy in certificate serial numbers if golang.org/x/crypto is not properly utilized. Fix: Verify that all random operations use crypto/rand instead of math/rand. Use adequate key lengths (2048-bit RSA minimum, preferably 4096-bit or ECDP-256+). Ensure proper certificate extensions and constraints are set. - Medium · Trust Store Modifications Without Verification —
truststore_darwin.go, truststore_linux.go, truststore_windows.go, truststore_java.go, truststore_nss.go. The tool modifies system trust stores across multiple platforms (Darwin, Linux, Windows, Java, NSS). Without code review, there's a risk of privilege escalation vulnerabilities or improper validation of trust store modifications, especially on privileged operations. Fix: Implement strict validation of all trust store paths before modification. Run with minimal required privileges. Consider requiring explicit user confirmation for trust store changes. Audit all shell command executions for injection risks. - Low · Missing Security Policy Documentation —
Repository root. No SECURITY.md or security policy file is visible in the repository structure, making it difficult for security researchers to responsibly disclose vulnerabilities. Fix: Create a SECURITY.md file following GitHub's security policy template with clear instructions for responsible vulnerability disclosure and contact information.
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.