Documentation
¶
Overview ¶
Package localca is a framework-free, mkcert-style local development certificate authority for Go tools. On first run it generates a per-machine ECDSA root CA, installs it into the host's system and NSS (Firefox/Chromium) trust stores, and mints short-lived leaf certificates for local hosts (localhost, 127.0.0.1, ::1, and LAN IPs) so a tool can serve browser-trusted HTTPS with zero manual setup.
The leaf is surfaced as a gitlab.com/phpboyscout/go/tls.Pair, so any transport that already consumes the shared TLS plumbing serves the trusted certificate unchanged. EnsureServed is the one-liner: ensure the root exists and is trusted, then return a ready-to-serve Pair.
The package is framework-free: it works from a typed Config, an injected github.com/spf13/afero.Fs for storage, and an optional *log/slog.Logger. The cross-OS trust-store install sits behind the TrustStore seam so the pure-crypto core is fully testable without touching a real trust store.
This is Layer 1 of a two-layer TLS strategy for the phpboyscout tool family: a per-machine local CA that works offline forever. The public ACME path (Layer 2) is a separate, downstream concern; this module remains the offline/CI/air-gapped fallback even once Layer 2 exists.
Security ¶
The root's private key never leaves the machine and is stored 0600. A shared or shipped CA key is a universal MITM key against every user and is never generated or bundled — every install mints its own root. Installing a system-trusted root requires OS elevation; that prompt is the consent gate, and the install is reversible via Authority.Uninstall.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
"localca: administrator privileges are required to install into the system trust " +
"store, but no elevation is available (not root, and no interactive terminal to prompt)",
)
ErrElevationUnavailable is returned when installing into the system trust store needs administrator privileges but none can be obtained interactively: the process is not root and there is no terminal (or no sudo) to prompt with. A consumer catches this to fall back — e.g. keryx drops to an untrusted self-signed cert (spec 0027 §5.4) — rather than hanging on a prompt that can never be answered.
Functions ¶
func EnsureServed ¶
func EnsureServed(ctx context.Context, cfg Config, hosts []string, opts ...Option) (gotls.Pair, error)
EnsureServed is the first-run one-liner: ensure the root exists and is trusted in every supported store, mint (or reuse a cached) leaf for hosts, persist it, and return a go/tls.Pair ready for any transport's ServerConfig. Idempotent: a second call with a still-installed root and a still-valid cached leaf re-prompts nothing and re-mints nothing.
Example ¶
ExampleEnsureServed shows the first-run one-liner: provision a per-machine local CA (installing it into the trust store on first run) and serve browser-trusted HTTPS. It compiles as documentation; it is not executed (it would mutate the host trust store).
package main
import (
"context"
"log"
"net/http"
"gitlab.com/phpboyscout/go/localca"
)
func main() {
cfg := localca.Config{DataDir: "/home/me/.myapp", AppName: "myapp"}
pair, err := localca.EnsureServed(context.Background(), cfg, []string{"localhost", "127.0.0.1"})
if err != nil {
log.Fatal(err)
}
tlsCfg, err := pair.ServerConfig()
if err != nil {
log.Fatal(err)
}
srv := &http.Server{Addr: ":8443", TLSConfig: tlsCfg}
log.Fatal(srv.ListenAndServeTLS("", ""))
}
Output:
Types ¶
type Authority ¶
type Authority interface {
// Installed reports where the root is currently trusted.
Installed(ctx context.Context) (TrustState, error)
// Install mints the root if absent and installs it into the given trust stores
// (all supported when none are given). Installing into the system store triggers
// the OS elevation prompt.
Install(ctx context.Context, stores ...Store) (TrustState, error)
// Uninstall removes the root from trust stores; with Purge it also deletes the
// stored key and cert.
Uninstall(ctx context.Context, opts ...UninstallOption) error
// Leaf returns a short-lived server certificate valid for hosts, signed by the
// root. It does not install the root; it errors if no root exists yet (use
// EnsureServed for the mint-and-install path).
Leaf(ctx context.Context, hosts ...string) (*cryptotls.Certificate, error)
}
Authority is a per-machine local CA that can install itself into the host's trust stores and issue leaf certificates for local hosts.
type Config ¶
type Config struct {
// DataDir is where the root key/cert and cached leaves live. Required. The key
// files are written 0600. Must be a stable per-machine location.
DataDir string
// AppName seeds the root CommonName ("<AppName> local CA (<user>@<host>)") so an
// installed root is self-evidently a per-machine dev identity. Defaults to "local".
AppName string
// Organization seeds the root subject Organization. Defaults to AppName.
Organization string
// RootTTL is the root CA validity. Zero uses defaultRootTTL (~10 years).
RootTTL time.Duration
// LeafTTL is the issued leaf validity. Zero uses defaultLeafTTL (90 days).
LeafTTL time.Duration
}
Config is the typed configuration for an Authority. It is owned by this module and carries no framework coupling: a GTB consumer decodes its own config section into this struct in its adapter; a standalone consumer fills it directly.
type Option ¶
type Option func(*options)
Option injects the runtime environment. All options are optional and nil-safe.
func WithLogger ¶
WithLogger attaches a structured logger. Nil is ignored (logging stays silent).
func WithTrustStore ¶
func WithTrustStore(ts TrustStore) Option
WithTrustStore swaps the cross-OS trust-store backend — the seam that keeps the crypto core testable without touching a real store.
type Store ¶
type Store int
Store identifies a trust store the root can be installed into.
const ( // StoreSystem is the OS system trust store (macOS Keychain, Linux // ca-certificates, Windows CryptoAPI). Installing here needs OS elevation. StoreSystem Store = iota // StoreNSS is the shared NSS database used by Firefox and Chromium. Install is // user-level but needs the platform certutil. StoreNSS )
type TrustState ¶
TrustState reports where the root is currently trusted.
type TrustStore ¶
type TrustStore interface {
// Install adds the root to the given stores (all supported when none are given)
// and returns the resulting state.
Install(root *x509.Certificate, stores ...Store) (TrustState, error)
// Uninstall removes the root from the given stores (all when none are given).
Uninstall(root *x509.Certificate, stores ...Store) error
// State reports where the root is currently trusted.
State(root *x509.Certificate) (TrustState, error)
}
TrustStore is the cross-OS trust-store backend seam. The default implementation wraps github.com/smallstep/truststore (mkcert's logic); tests inject a fake so the crypto core needs no real store. Implementations act on the root's public certificate only — a trust store never holds the private key.
type UninstallOption ¶
type UninstallOption func(*uninstallOpts)
UninstallOption tunes Authority.Uninstall.
func Purge ¶
func Purge() UninstallOption
Purge deletes the stored root key/cert after removing it from trust stores.