From 44dea49fd10ea01886d1498b38e8a688cffe7b67 Mon Sep 17 00:00:00 2001 From: Gitead Date: Sun, 12 Jul 2026 17:34:12 -0400 Subject: [PATCH] docs: add LAN HTTPS setup guide (nginx + certbot + Cloudflare DNS-01) Documents the trusted-certificate setup used for local staff access, needed because the Screen Capture API (Copy Agent screenshots) requires a secure context. --- README.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/README.md b/README.md index dd3397d3..db5df65a 100644 --- a/README.md +++ b/README.md @@ -508,6 +508,110 @@ Each office runs its own `cloudflared` tunnel on its own PC. Ports never conflic --- +## LAN HTTPS Setup (Trusted Certificate for Local Access) + +Some browser features — notably the Screen Capture API used by the Copy Agent's screenshot +tool — only work in a "secure context" (`https://`, or the literal hostname `localhost`). Plain +`http://:3000` doesn't qualify, so staff would see the feature report itself as +unsupported. + +The fix used here: a real, publicly-trusted certificate (Let's Encrypt) for a subdomain whose +DNS record points at this office's private LAN IP. Since the cert is issued via a DNS-01 +challenge (not a live connection to the server), staff PCs need zero configuration — no local CA +to install, unlike a self-signed/mkcert certificate. + +**How it works:** +- DNS: `local-.mydentalofficemanagement.com` → `A` record → this PC's LAN IP, **unproxied** + ("DNS only" in Cloudflare — proxying doesn't work for private IPs) +- nginx terminates TLS using the Let's Encrypt cert and only accepts connections from the office + subnet (`allow ; deny all;`) +- Staff browse to `https://local-.mydentalofficemanagement.com` instead of the raw IP + +This is separate from the Cloudflare Tunnel above — the tunnel is for public access (e.g. Twilio +webhooks), this is for trusted HTTPS on the local network only. + +--- + +### Step 1 — Create the DNS record (in Cloudflare, not the registrar) + +In the Cloudflare dashboard for `mydentalofficemanagement.com` → DNS → Add record: +- Type: `A`, Name: `local-` (e.g. `local-summit`), Content: this PC's LAN IP +- **Proxy status: DNS only** (grey cloud) — required, Cloudflare can't proxy a private IP + +### Step 2 — Install nginx (if not already done in Step 12 above) + +```sh +sudo apt update +sudo apt install -y nginx +``` + +> If another web server (e.g. Apache) is already bound to port 80/443, nginx will fail to start. +> Check with `sudo ss -tlnp | grep -E ':80|:443'` and disable the conflicting service first. + +### Step 3 — Install certbot with the Cloudflare DNS plugin + +```sh +sudo apt update +sudo apt install -y certbot python3-certbot-dns-cloudflare +``` + +### Step 4 — Create a scoped Cloudflare API token + +Cloudflare dashboard → profile icon → **My Profile** → **API Tokens** → **Create Token** → use the +**"Edit zone DNS"** template, restricted to **Specific zone → mydentalofficemanagement.com**. + +Save it in a credentials file, root-only: + +```sh +sudo mkdir -p /etc/letsencrypt +sudo nano /etc/letsencrypt/cloudflare.ini +``` + +```ini +dns_cloudflare_api_token = +``` + +```sh +sudo chmod 600 /etc/letsencrypt/cloudflare.ini +``` + +> The same token can be reused across offices (Cloudflare tokens are scoped to the whole zone, +> not a single subdomain) — but issuing one token per server makes it easy to revoke just one if +> a machine is ever decommissioned. + +### Step 5 — Issue the certificate + +```sh +sudo certbot certonly --dns-cloudflare \ + --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ + -d local-.mydentalofficemanagement.com +``` + +Certbot adds a temporary DNS TXT record via the API to prove domain ownership, then removes it. +The cert lands at `/etc/letsencrypt/live/local-.mydentalofficemanagement.com/` and +auto-renews via a systemd timer — no manual renewal steps. + +### Step 6 — Point nginx at the new cert + +Update the LAN server block in `nginx.conf` (`server_name`, `ssl_certificate`, +`ssl_certificate_key`, and the `allow ;` line) for this office, then reinstall and reload: + +```sh +sudo cp nginx.conf /etc/nginx/sites-available/dental-app +sudo nginx -t && sudo systemctl reload nginx +``` + +### Step 7 — Allow the new hostname in Vite and backend CORS + +- `apps/Frontend/vite.config.ts` → add the hostname to `server.allowedHosts` +- `apps/Backend/.env` → add `https://local-.mydentalofficemanagement.com` to + `FRONTEND_URLS` + +Staff can now use `https://local-.mydentalofficemanagement.com` with a trusted padlock — +no certificate warnings, no CA install on any PC. + +--- + ## Twilio In-Browser Calling Setup (Dial Pad) The dial pad on the Patient Connection page lets staff make real phone calls directly through the browser (mic + speaker) using Twilio Voice SDK. One-time setup is required in the Twilio Console.