Data directory layout¶
Everything localca persists lives in Config.DataDir. Nothing is written outside it,
and no keychain, secret store or registry is used.
Files written under DataDir¶
| File | Mode | Contents | Created by |
|---|---|---|---|
rootCA.pem |
0644 |
The root certificate, PEM CERTIFICATE |
first Install or EnsureServed |
rootCA-key.pem |
0600 |
The root private key, PEM EC PRIVATE KEY (SEC 1, P-256) |
first Install or EnsureServed |
leaf.pem |
0644 |
The cached server certificate | EnsureServed |
leaf-key.pem |
0600 |
The cached server key, PEM EC PRIVATE KEY |
EnsureServed |
trust-install.json |
0644 |
Which stores this root was installed into | the default trust-store backend |
rootCA.lock |
0600 |
Empty; held locked while the root or the cached leaf is minted and saved | first Install or EnsureServed, OS filesystem only |
The directory itself is created with mode 0700.
Files a Private CA writes instead¶
A Private CA (from NewPrivate) keeps its root under different names, so an Authority
pointed at the same directory never reads, and never installs, a private root.
| File | Mode | Contents | Created by |
|---|---|---|---|
privateCA.pem |
0644 |
The private root certificate | its first Root, Leaf or SignClient |
privateCA-key.pem |
0600 |
The private root key, PEM EC PRIVATE KEY |
the same |
privateCA.lock |
0600 |
Empty; held locked while the root is minted | the same, OS filesystem only |
It writes nothing else: its server leaves and client certificates are returned to the
caller, not cached. NewPrivate refuses a directory that already holds rootCA.pem.
privateCA-key.pem signs the client certificates of every machine enrolled with it, so it
needs the same care as rootCA-key.pem. To renew a private root, stop the service and delete
both privateCA files. The next call mints a new root, and every enrolled machine must enrol
again, because each one pinned the old root.
Authority.Leaf writes nothing; it returns an in-memory certificate. Only EnsureServed
maintains the cached leaf files.
What is safe to copy, and what is not¶
rootCA.pem is a public certificate. Copying it to another machine and importing it there
is the supported way to make a second device trust your certificates.
rootCA-key.pem is the signing key for a certificate authority your machine trusts. Anyone
holding it can mint a certificate for any hostname that your machine will accept. It must
not be copied off the machine, committed, backed up to a shared location, or baked into an
image. That is the whole reason localca mints a fresh root per machine rather than
shipping one; see the trust model.
trust-install.json¶
The OS system store has no portable "is this certificate trusted?" query, so localca
records its own installs here and treats that record as the answer. The fingerprint ties
the record to one specific root, so a re-minted root is correctly reported as not installed.
Consequences worth knowing:
- Deleting this file makes
localcabelieve the root is not installed. The nextEnsureServedre-runs the install and prompts for elevation again. The install itself is idempotent, so no duplicate entry is created. - Removing the root from the OS store by hand does not update this file.
localcakeeps reportingSystem: trueand will not reinstall until the file is deleted or the root is re-minted. - The NSS half of
TrustStateis never read from this file; it is queried live.
Temporary files during a write¶
Every file is written by creating .<name>.<random>.tmp in the same directory, setting the
final mode on it, and renaming it over the target. A rename within one filesystem is atomic,
so a crash mid-write leaves either the complete old file or the complete new one, never a
truncated key. The temporary file is removed if any step fails.
If you find a stale .rootCA-key.pem.*.tmp in the data directory, a write was interrupted
in a way that also prevented cleanup. It is safe to delete.
Concurrent first runs¶
Two processes that provision the same DataDir at once mint exactly one root between
them. The first to take an exclusive OS lock on rootCA.lock mints and saves the root;
the other waits, then loads that root instead of minting its own. Every caller ends up
holding the root on disk, which is the only one Uninstall can later remove.
The cached leaf is minted under the same lock. Two EnsureServed calls that both find no
usable leaf take turns: the second re-checks the cache once it holds the lock and reuses
the leaf the first one saved, so leaf.pem and leaf-key.pem always belong to the same
mint. The key is written before the certificate, so a reader never finds the certificate
without its key.
The OS releases the lock when the holding process exits, so a crash never leaves the
directory locked, and rootCA.lock itself is left in place and is safe to delete when
nothing is running. A filesystem passed with WithFS other than afero.NewOsFs() is
serialised within one process only.
What survives which call¶
| Call | rootCA*.pem |
leaf*.pem |
trust-install.json |
|---|---|---|---|
Install |
created if absent | untouched | written when StoreSystem is targeted |
EnsureServed |
created if absent | created or refreshed | written when it installs |
Uninstall |
kept | kept | set to system: false |
Uninstall with Purge() |
deleted | kept | set to system: false |
What Purge leaves behind¶
Purge() deletes rootCA.pem and rootCA-key.pem only. The cached leaf.pem and
leaf-key.pem are left in place, and they are still signed by the root you just deleted.
If you provision again in the same data directory, EnsureServed mints a new root but
reuses that stale leaf: it covers the same hosts and has not expired, so the cache check
passes. The server then presents a certificate chaining to a root that is no longer
installed anywhere, and every client rejects it, until the leaf drifts out of the cache
(a different host list, or the seven-day renewal window).
Delete the leaf files yourself when you purge:
Or remove the whole data directory, which is the cleanest reset:
Recovering from a half-written root¶
localca reads the root certificate and key as a pair. If rootCA.pem exists but
rootCA-key.pem does not, every call fails with:
localca writes the key before the certificate, so an interrupted or concurrent write
cannot produce this state. It comes from deleting the key by hand, or from v0.2.2 or
earlier, which saved the certificate first.
This is deliberate. A certificate with no key is not something to silently replace, because
the matching root may be installed in your trust store. Recover by removing the root from
the trust stores if it is still there, then deleting rootCA.pem so the next call mints a
fresh pair.
A corrupt leaf is handled differently: an unparseable leaf.pem is treated as no cached
leaf at all and is quietly re-minted.