Issues, renews, and publishes the *.local.eca.dev wildcard TLS certificate
that ECA's remote web-control server uses for HTTPS. The certificate is served
at https://tls.eca.dev/ via GitHub Pages; ECA fetches and caches it at
runtime (it is no longer bundled in the ECA binary).
local.eca.dev is delegated in Cloudflare to sslip.io's nameservers so that
192-168-1-42.local.eca.dev resolves to the LAN IP 192.168.1.42. The wildcard
cert covers those hostnames, giving browsers a valid HTTPS connection to a
private IP (no mixed-content blocking).
That delegation also means Cloudflare can't serve the ACME _acme-challenge
TXT record (sslip.io is authoritative for everything under local.eca.dev, and
it can't serve the bare _acme-challenge.local.eca.dev). So issuance:
- Backs up + removes the
localNS delegation in Cloudflare. - Runs lego (DNS-01 via the Cloudflare API) to publish the TXT and issue the cert — now servable because Cloudflare is authoritative again.
- Always restores the NS delegation (even on failure).
- Publishes the new PEMs to
tls.eca.dev(gh-pages) and commits them tomain.
While the delegation is removed (~6 min, scheduled off-peak) new
*.local.eca.dev lookups fail; existing TLS sessions and the Tailscale/localhost
paths are unaffected.
The private key is intentionally public.
*.local.eca.devonly resolves to private LAN IPs, so publishing the key just enables valid HTTPS to a private address for everyone without per-user setup.
-
Create the repo
editor-code-assistant/eca-tlsand push this directory.public/is pre-seeded with the current valid cert. -
Cloudflare API token (My Profile → API Tokens → Create) scoped to:
Zone→DNS→EditZone→Zone→Read- Zone Resources →
Include→Specific zone→eca.dev
-
Repo secrets (Settings → Secrets and variables → Actions):
CF_API_TOKEN— the token aboveCF_ZONE_ID— theeca.devzone id (Cloudflare dashboard → eca.dev → Overview)ACME_EMAIL— email for Let's Encrypt expiry notices (set a monitored one!)ALERT_WEBHOOK— (optional) Slack/Discord webhook for failure alerts
-
GitHub Pages (Settings → Pages): set source to the
gh-pagesbranch, root. (It's created by the first workflow run.) -
Cloudflare DNS: add
tlsCNAME→editor-code-assistant.github.io.- DNS-only (grey cloud) lets GitHub issue the
tls.eca.devcert. - Proxied (orange) also works (Cloudflare serves an edge cert for
*.eca.dev).
- DNS-only (grey cloud) lets GitHub issue the
-
First run: trigger the workflow manually (Actions → Renew *.local.eca.dev cert → Run workflow). With a cert >30 days from expiry it just publishes the seeded cert, bringing
tls.eca.devlive. Verify:curl -sS https://tls.eca.dev/local-eca-dev-fullchain.pem | openssl x509 -noout -subject -enddate
Only after tls.eca.dev serves a valid cert should the ECA change that drops the
bundled cert be released.
The workflow runs weekly and renews only when the published cert is within 30
days of expiry; otherwise it republishes the current one. workflow_dispatch
with force: true renews immediately.
The NS-restore step runs always(), so the delegation should self-heal. Verify:
dig +short A 192-168-1-42.local.eca.dev # should return 192.168.1.42
If it doesn't resolve, restore the delegation manually in Cloudflare — add three
NS records on local: ns-do-sg.sslip.io, ns-hetzner.sslip.io,
ns-ovh.sslip.io. Or re-run the workflow (it backs up/restores idempotently).