README
ΒΆ
homepki
A simple PKI management tool for local development. Create and manage three-tier certificate authorities, intermediate CAs, and TLS certificates with a single CLI.
π Documentation Β· bcollard.github.io/homepki
Install
brew tap bcollard/homepki
brew install --cask homepki
Quick Start
# Create root CA
homepki root-ca --domain runlocal.dev
# Create intermediate CA
homepki intermediate-ca --domain runlocal.dev --name bu1
# Create server certificate
homepki server-cert --domain runlocal.dev --intermediate bu1 --server kong-gateway
# Create client certificate
homepki client-cert --domain runlocal.dev --intermediate bu1 --client my-client
# Trust the root CA in the system, Firefox and Java trust stores (macOS, Linux)
homepki trust install --domain runlocal.dev
Certificates and keys are stored in ~/.homepki by default. homepki is a single Go binary with no runtime dependency: keys, signing and chain verification use Go's crypto/x509, so openssl does not need to be installed.
Configuration
Override the default storage location using:
- The
--workdirflag:homepki root-ca --domain runlocal.dev --workdir /path/to/pki - The
HOMEPKI_WORKDIRenvironment variable:export HOMEPKI_WORKDIR=/path/to/pki homepki root-ca --domain runlocal.dev
Agent Skill (AI coding tools)
homepki ships an Agent Skill that teaches AI coding tools how to drive homepki β issuing TLS and mTLS material for scripts, demos, and tests. Install it once and every future agent session knows how to use homepki without you explaining it each time:
homepki skill install # β ~/.claude/skills/homepki/SKILL.md
homepki skill install --force # overwrite an existing copy (e.g. after upgrading homepki)
homepki skill path # print the install path
homepki skill install --print # emit the skill to stdout (pipe it anywhere)
The skill is embedded in the binary, so no download is needed. Start a new agent session after installing to pick it up.
Overview
homepki implements a three-tier PKI structure:
- Root CA: The top-level certificate authority
- Intermediate CA: Organization/tenant-specific intermediate certificate authorities
- End-entity certificates: Server and client certificates for services
Build from Source
go build -o homepki
Project Structure
.
βββ main.go # Entry point
βββ skill.go # Embeds SKILL.md into the binary
βββ SKILL.md # Agent Skill definition (single source of truth)
βββ go.mod
βββ cmd/
β βββ root.go # CLI setup, --workdir flag
β βββ root_ca.go # root-ca command (generate + list)
β βββ intermediate_ca.go # intermediate-ca command (generate + list)
β βββ server_cert.go # server-cert command (generate + list)
β βββ client_cert.go # client-cert command (generate + list)
β βββ sign.go # sign command (external CSRs)
β βββ trust.go # trust install/uninstall/status (system, NSS, Java)
β βββ revoke.go # revoke + crl commands
β βββ leaf.go # shared server/client leaf issuance
β βββ paths.go # shared flags, CA file locations, helpers
β βββ skill.go # skill install + path
βββ pkg/
βββ pki/
βββ pki.go # file helpers, chain verification, SAN parsing
βββ certs.go # certificate issuance (crypto/x509)
βββ keys.go # --key-type β key generation, PEM key I/O
βββ constraints.go # --name-constraint parsing
βββ csr.go # CSR loading and subject policy checks
βββ crl.go # CRL signing, loading, revocation reasons
βββ pkcs12.go # PKCS#12 bundles
βββ trust.go # trust store detection and commands
Usage Guide
1. Creating a Root CA
homepki root-ca --domain runlocal.dev
# List root CAs (table, default)
homepki root-ca list
# List as JSON
homepki root-ca list -o json
This creates:
- Root CA directory structure
- Root CA private key and self-signed certificate
2. Creating an Intermediate CA
homepki intermediate-ca --domain runlocal.dev --name bu1
# List intermediate CAs (table, default)
homepki intermediate-ca list --domain runlocal.dev
# List as JSON
homepki intermediate-ca list --domain runlocal.dev -o json
This creates:
- Intermediate CA private key and certificate signed by the root CA
- Certificate chain file (
{name}-intermediate-ca-chain.crt)
3. Creating Server Certificates
homepki server-cert --domain runlocal.dev --intermediate bu1 --server kong-gateway
# List server certificates (table, default)
homepki server-cert list --domain runlocal.dev --intermediate bu1
# List as JSON
homepki server-cert list --domain runlocal.dev --intermediate bu1 -o json
4. Creating Client Certificates
homepki client-cert --domain runlocal.dev --intermediate bu1 --client my-service
# List client certificates (table, default)
homepki client-cert list --domain runlocal.dev --intermediate bu1
# List as JSON
homepki client-cert list --domain runlocal.dev --intermediate bu1 -o json
5. Signing an External CSR
Sign a request generated elsewhere β the private key never reaches homepki:
openssl req -new -nodes -newkey rsa:2048 -keyout my-service.key -out my-service.csr \
-subj "/O=runlocal-dev/OU=bu1/CN=my-service.bu1.runlocal.dev" \
-addext "subjectAltName=DNS:my-service.bu1.runlocal.dev,DNS:my-service.local"
homepki sign --domain runlocal.dev --intermediate bu1 --csr my-service.csr
# As a client certificate, under a chosen name
homepki sign --domain runlocal.dev --intermediate bu1 --csr my-client.csr \
--type client --name my-client
# Write the certificate outside the PKI tree
homepki sign --domain runlocal.dev --intermediate bu1 --csr my-service.csr \
--out ./my-service.crt
The request's subject must carry the root CA's organization (O=, dots replaced by dashes) and the intermediate's organizational unit (OU=). Mismatches are rejected with the exact -subj to use printed in the error. Subject Alternative Names are copied from the request; every other requested extension is ignored, so a request cannot ask to be a CA.
Trusting the Root CA
Add the root CA to this machine's trust stores so browsers, curl and JVMs accept certificates issued under it:
homepki trust install --domain runlocal.dev # every store available on this host
homepki trust install --domain runlocal.dev --store nss,java # skip the system store (no sudo)
homepki trust status # every root CA in the workdir
homepki trust status --domain runlocal.dev -o json
homepki trust uninstall --domain runlocal.dev
--store |
Covers | Needs |
|---|---|---|
system |
macOS System keychain; Linux anchors on Debian/Ubuntu/Alpine, Fedora/RHEL, Arch, openSUSE | sudo |
nss |
Firefox profiles, and Chrome/Chromium's ~/.pki/nssdb on Linux |
certutil (brew install nss, apt install libnss3-tools) |
java |
$JAVA_HOME cacerts |
keytool; sudo only if cacerts is not writable |
Without --store, homepki uses the system store plus every NSS database and JDK it finds. status needs no privileges. Servers must still present the intermediate chain file, or clients cannot build the path from the leaf to the trusted root.
Key Types
Every generate command takes --key-type: rsa (2048-bit, the default), ecdsa / ecdsa-p256, ecdsa-p384, ecdsa-p521, or ed25519. Tiers are independent, so an ECDSA leaf under an RSA intermediate is fine.
homepki intermediate-ca --domain runlocal.dev --name bu1 --key-type ecdsa
homepki server-cert --domain runlocal.dev --intermediate bu1 --server gw --key-type ecdsa-p384
The signature digest follows the issuing CA's key: SHA-256 for RSA and P-256, SHA-384 for P-384, SHA-512 for P-521. Browsers reject Ed25519 certificates; use ed25519 for service-to-service TLS only.
Validity
Every command that signs takes --validity, in days (90d) or as a Go duration (24h). Defaults are 2190 days for CAs and 365 days for leaves. A certificate is never valid past its issuer: a longer request is capped, with a note.
homepki server-cert --domain runlocal.dev --intermediate bu1 --server gw --validity 24h
PKCS#12
--pkcs12 on server-cert and client-cert also writes <name>.p12 with the key, certificate and CA chain. The password is changeit unless --pkcs12-password sets another. The encoding is readable by Java, macOS Keychain, Windows and OpenSSL 3.
homepki client-cert --domain runlocal.dev --intermediate bu1 --client my-client --pkcs12
Revocation
revoke adds a leaf to its intermediate's CRL; crl writes or refreshes the CRLs without revoking anything.
homepki crl --domain runlocal.dev --intermediate bu1
homepki revoke --domain runlocal.dev --intermediate bu1 --client my-client --reason keyCompromise
This writes bu1-intermediate-ca.crl, ca/<root>-root-ca.crl, and bu1-crl-chain.crl (both CRLs, for servers such as nginx that check every tier). CRLs are valid for 365 days by default (--validity). client-cert list and server-cert list report revoked leaves as invalid.
Name Constraints
root-ca and intermediate-ca take a repeatable --name-constraint that limits the names the CA may issue for. Values use openssl syntax β permitted; or excluded;, then DNS, IP, email or URI. permitted; may be omitted and IP ranges accept CIDR.
homepki root-ca --domain klimax.internal --name-constraint "permitted;DNS:.klimax.internal"
homepki intermediate-ca --domain klimax.internal --name bu1 \
--name-constraint "permitted;DNS:.bu1.klimax.internal" --name-constraint "IP:10.0.0.0/8"
server-cert, client-cert and sign verify every new certificate against its chain before writing it, so a name outside the constraints is refused and nothing is written. Constrain a root before trusting it system-wide: it then cannot vouch for any other site, even if its key leaks.
Re-issuing
Generate commands refuse to overwrite existing material: a re-run exits 1, prints what is in the way, and changes nothing. --force replaces it.
homepki server-cert --domain runlocal.dev --intermediate bu1 --server kong-gateway --force
There is no CA database, so the same subject can be issued any number of times. For a CA tier, --force orphans every certificate beneath it β check the tier below the one you replaced, since it is the only one whose chain flips to invalid.
Listing and Chain Verification
All list subcommands verify the certificate's chain of trust and support two output formats via -o/--output:
| Format | Description |
|---|---|
table |
Human-readable table with coloured chain status (default) |
json |
Machine-readable JSON array, no extra output |
Chain validity is checked at list time:
root-ca listβ verifies each root cert is validly self-signedintermediate-ca listβ verifies the intermediate was signed by the root CAserver-cert list/client-cert listβ verifies the leaf cert chains through the intermediate to the root CA
Example JSON output:
[
{
"name": "kong-gateway.crt",
"expires": "2027-04-01",
"days_left": 360,
"chain_valid": true
},
{
"name": "old.crt",
"expires": "2025-01-01",
"days_left": -270,
"chain_valid": false,
"chain_error": "x509: certificate has expired or is not yet valid"
}
]
Security Features
- 2048-bit RSA keys by default, or ECDSA P-256/P-384/P-521 or Ed25519 via
--key-type - Private keys written as PKCS#8 PEM, mode 600, in 700
private/directories - Random 128-bit serial numbers
- Name constraints on either CA tier, enforced before a leaf is written
- Chain verification on every issue and every
listcommand, including revocation for leaves
File Organization
Root CA Structure
{domain-name}/
βββ ca/
β βββ {domain-name}-root-ca.crt # Root certificate
β βββ {domain-name}-root-ca.crl # Root CRL (after revoke / crl)
β βββ private/ # Protected private keys
β βββ {domain-name}-root-ca.key # Root private key
Intermediate CA Structure
{domain-name}/
βββ {organization}/
βββ {org}-intermediate-ca.crt # Intermediate certificate
βββ {org}-intermediate-ca-chain.crt # Certificate chain
βββ {org}-intermediate-ca.crl # Intermediate CRL (after revoke / crl)
βββ {org}-crl-chain.crl # Intermediate + root CRLs
βββ server-tls/ # Server certificates
βββ client-tls/ # Client certificates
βββ private/ # Protected private keys
βββ {org}-intermediate-ca.key # Intermediate private key
Defaults
- Key: 2048-bit RSA (
--key-type ecdsafor P-256,ecdsa-p384,ecdsa-p521,ed25519) - Digest: follows the issuer's key β SHA-256 (RSA, P-256), SHA-384 (P-384), SHA-512 (P-521)
- Serial numbers: random 128-bit
- Validity: 365 days for leaf certs, 2190 days (~6 years) for CAs, 365 days for CRLs (
--validityoverrides) - Extensions: Proper X.509 extensions for CA and end-entity certificates
Best Practices
- Secure storage: Keep private keys in secure, encrypted storage
- Access control: Limit access to CA private keys
- Backup: Regularly backup CA certificates and keys
- Rotation: Plan for certificate renewal and CA rotation
- Monitoring: Track certificate expiration dates
Troubleshooting
Common Issues
- Permission denied: Ensure proper file permissions on private directories
- Certificate validation: Check certificate chains and trust relationships
- Expired certificates: Monitor and renew certificates before expiration
Verification Commands
These use openssl, which homepki itself does not need.
# Inspect a certificate
openssl x509 -in certificate.crt -text -noout
# Manually verify a certificate chain
openssl verify -CAfile root-ca.crt -untrusted intermediate-ca.crt end-entity.crt
# Check a private key
openssl rsa -in private-key.key -check
License
MIT β see LICENSE.
Contributing
- Test changes thoroughly in isolated environments
- Follow existing naming conventions
- Ensure security best practices are maintained
Documentation
ΒΆ
There is no documentation for this package.