Add deployment architecture diagram and update README

Mermaid flowchart covers: GoDaddy DDNS, pfSense NAT + split DNS,
Let's Encrypt DNS-01, nginx SSL termination, Docker host-network
containers, Proton Bridge SMTP relay, and Gitea git workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 16:29:50 +00:00
parent 92f55f61d1
commit bdab4de971

136
README.md
View File

@@ -1,72 +1,134 @@
# www.kenjim.com # www.kenjim.com
Static website served via nginx Docker container on zet.home.arpa, proxied through nginx SSL termination at `https://www.kenjim.com`. KenJim Technologies marketing site — React SPA with Express contact form backend, deployed on **zet.home.arpa** behind nginx SSL termination.
## Development Workflow ## Deployment Architecture
Edit on Mac → push to Gitea → pull on zet → site updates live. ```mermaid
flowchart TD
subgraph internet["Internet"]
browser(["Client browser"])
godaddy["GoDaddy DNS\nwww.kenjim.com CNAME → lair.kenjim.com\nlair.kenjim.com A → WAN IP (dynamic)"]
le["Let's Encrypt CA"]
end
``` subgraph pfsense["pfSense · home gateway"]
Mac Gitea (git.kenjim.com) zet.home.arpa ddns["DDNS client\nupdates lair.kenjim.com A record\nwhen WAN IP changes"]
git push │ │ nat["NAT / Port Forward\nWAN :80 / :443 → zet :80 / :443"]
├─────────────────────────────────►│ │ unbound["Unbound DNS Resolver\nwww / git / kenji.kenjim.com\n→ 172.27.0.35 (split DNS — bypasses NAT)"]
│ git pull │ end
│ ├───────────────────────────►│
│ │ │ docker compose up -d subgraph zet["zet.home.arpa · 172.27.0.35"]
subgraph ssl["SSL — acme.sh + Let's Encrypt"]
acme["acme.sh\nDNS-01 challenge via GoDaddy API"]
cert["/etc/nginx/ssl/kenjim.com/\nfullchain.pem · key.pem\nauto-renewed by cron"]
end
subgraph nginx_host["nginx · host process"]
nginxsrv[":80 → redirect HTTPS\n:443 · SSL termination\nproxy_pass → localhost:8080"]
end
subgraph docker["Docker · network_mode: host"]
fe["www-kenjim-frontend\nnginx :8080\nReact SPA (pre-built image)\n/api/* proxied → :3001"]
api["www-kenjim-api\nExpress.js :3001\nPOST /contact handler"]
end
bridge["Proton Mail Bridge\n127.0.0.1:1025 SMTP relay"]
gitea[("Gitea · git.kenjim.com\nkenjim/www.kenjim.com")]
end
protonmail["Proton Mail\ninfo@kenjim.com → kenji@kenjim.com"]
browser -->|"DNS lookup"| godaddy
browser -->|"HTTPS :443"| nat
nat -->|"port forward"| nginxsrv
unbound -.->|"LAN clients resolve direct"| nginxsrv
nginxsrv -->|"proxy_pass :8080"| fe
fe -->|"proxy /api/*"| api
api -->|"SMTP 127.0.0.1:1025"| bridge
bridge -->|"relay via Proton servers"| protonmail
acme <-->|"set DNS TXT record\nfor domain validation"| godaddy
acme <-->|"certificate issuance"| le
acme -->|"writes cert"| cert
cert -->|"loaded by"| nginxsrv
ddns -->|"keeps A record current"| godaddy
gitea -->|"git pull · docker compose up --build"| fe
gitea -->|"git pull · docker compose up --build"| api
``` ```
## Structure ## Structure
``` ```
www.kenjim.com/ www.kenjim.com/
├── docker-compose.yml # runs nginx:alpine on host port 8080 ├── docker-compose.yml # both services run network_mode: host
── html/ # web root — edit files here ── frontend/
── index.html ── Dockerfile # node:20 build → nginx:alpine serve
│ ├── nginx.conf # :8080, SPA fallback, /api/ proxy → :3001
│ └── src/ # React source (Vite)
└── backend/
├── Dockerfile
└── server.js # Express, POST /contact → nodemailer
``` ```
All files under `html/` are served directly. No build step required for static content. ## Development Workflow
Edit on Mac → push to Gitea → pull + rebuild on zet:
```
Mac Gitea (git.kenjim.com) zet.home.arpa
│ git push │ │
├──────────────────────────────►│ │
│ │ git pull │
│ ├─────────────────────────►│
│ │ │ docker compose up --build -d
```
## Running on zet ## Running on zet
```bash ```bash
cd ~/workspace/src/personal/www.kenjim.com cd ~/workspace/src/personal/www.kenjim.com
docker compose up -d # start docker compose up --build -d # build images and start
docker compose down # stop docker compose down # stop
docker compose logs -f # watch logs docker compose logs -f # watch logs
docker logs www-kenjim-api # backend only
docker logs www-kenjim-frontend # frontend only
``` ```
## Deploying Changes ## Environment
On zet after pushing from Mac: `.env` lives on zet only (not in git). Required vars:
| Variable | Example | Notes |
|---|---|---|
| `SMTP_HOST` | `127.0.0.1` | Proton Bridge SMTP host |
| `SMTP_PORT` | `1025` | Proton Bridge SMTP port |
| `SMTP_SECURE` | `false` | Bridge uses STARTTLS, not SSL-on-connect |
| `SMTP_USER` | `kenji@kenjim.com` | Bridge auth credential |
| `SMTP_PASS` | `<bridge-password>` | From Bridge SMTP settings |
| `FROM_EMAIL` | `info@kenjim.com` | Sender address shown in emails |
| `CONTACT_TO` | `info@kenjim.com` | Destination for contact form submissions |
## SSL Certificate
Issued by Let's Encrypt via DNS-01 challenge using the GoDaddy API. Managed by acme.sh on zet:
```bash ```bash
cd ~/workspace/src/personal/www.kenjim.com # manual renewal (cron handles this automatically)
git pull ~/.acme.sh/acme.sh --renew -d kenjim.com --wildcard --dns dns_gd
# nginx:alpine picks up file changes immediately via volume mount — no restart needed
``` ```
If `docker-compose.yml` itself changes, recreate the container: Cert location: `/etc/nginx/ssl/kenjim.com/`
```bash
docker compose up -d
```
## How It Connects to nginx
The container listens on host port `8080`. The nginx reverse proxy on zet routes `https://www.kenjim.com``http://127.0.0.1:8080`.
See `~/workspace/src/personal/appa-net/zet.home.arpa/nginx/kenjim.conf` for the nginx config.
## Mac Setup (first time) ## Mac Setup (first time)
```bash ```bash
# Clone the repo on your Mac
git clone http://kenjim@git.kenjim.com/kenjim/www.kenjim.com.git git clone http://kenjim@git.kenjim.com/kenjim/www.kenjim.com.git
cd www.kenjim.com cd www.kenjim.com
# edit frontend/src, then:
# Edit html/ files, then: git add . && git commit -m "..." && git push
git add .
git commit -m "your message"
git push
``` ```