Trusting a certificate authority you created yourself
Why .test can never receive a publicly-trusted certificate, what a certificate authority actually consists of, why leaf certificates expire after 396 days, how to trust a root without administrator rights, and the risk you take on by doing it.
A development certificate is a strange object. It has to be trusted enough that a browser stops complaining, and untrusted enough that it cannot be used against anybody. Getting that balance right is mostly a matter of understanding what a certificate authority actually is.
You cannot get a public certificate for .test
Start here, because it rules out the obvious approach.
.test is one of four TLDs reserved by RFC 6761 for local use, alongside
.example, .invalid and .localhost. Reserved means it will never be
delegated in the public DNS root, and that has a consequence people usually
discover the hard way: no public certificate authority can ever issue a
certificate for it.
Not "does not at the moment". Cannot. Every ACME challenge works by proving you
control a domain, whether by serving a file at a URL or publishing a DNS
record. Nobody controls myapp.test, because myapp.test does not exist
anywhere outside your own hosts file. There is nothing to prove.
Let's Encrypt is not an option here, and neither is any other public CA. That is not a gap in their coverage; it is the reservation working as intended.
What a certificate authority actually is
The remaining option sounds heavier than it is, because "certificate authority" sounds institutional. It is not. A CA is a key pair whose public half sits in a trust store, plus the discipline not to sign things carelessly.
So Libxa Desktop generates one on your machine:
- A root key and a self-signed root certificate, valid for ten years, created once and kept in the app's own data directory.
- A leaf certificate per site, signed by that root, naming the site's domain,
its wildcard,
localhostand127.0.0.1in the subject alternative name.
The browser trusts the leaf because it trusts the root that signed it. That is the whole mechanism. A self-signed certificate fails not because it is cryptographically weaker but because nothing in the chain is in your trust store.
396 days, and why not longer
Leaf certificates are issued for 396 days.
Since 2020, Chrome and Safari reject any publicly-trusted certificate whose lifetime exceeds 398 days, and browsers apply the same rule to certificates from a local root. A certificate issued for five years, which is otherwise a perfectly reasonable thing to want for a development machine, is rejected outright with an error that does not obviously point at its lifetime.
396 leaves two days of margin against clock skew and rounding. Renewal runs a month before expiry, so certificates are replaced well before anything notices.
The root itself is valid for ten years, because replacing it means re-trusting it, and that is a thing to do rarely.
Trusting it without administrator rights
The step people expect to require elevation does not.
Windows has a per-user certificate store and a machine-wide one. Installing a root into the machine-wide store needs administrator rights. Installing into the current user's store does not:
certutil -user -addstore Root <path to the root certificate>
Certificates in the user store are trusted by that user's browsers and by nothing else, which is exactly the scope a development root should have. There is no reason for a certificate you created to test a service worker to be trusted by every account and every service on the machine.
So the app never runs as administrator to do this, and one button removes the root again.
What this does not protect against
Being straightforward about the risk, since it is a real one.
Anyone who can read the root's private key can sign a certificate for any domain, and your browser will accept it. That key sits in the app's data directory under your user profile, protected by the same filesystem permissions as everything else there. On a machine you control, running code you chose to run, that is a reasonable place for it. On a shared machine, or one where you run untrusted code, it is a key worth thinking about.
This is inherent to local CAs rather than specific to this one. Every tool in this category, mkcert included, makes the same trade. It is worth making knowingly.
The scope limits the damage: the root is in one user's store on one machine, it can be removed with one button, and it expires on its own.