Files
www.kenjim.com/README.md
Kenji M bdab4de971 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>
2026-06-19 16:29:50 +00:00

4.7 KiB

www.kenjim.com

KenJim Technologies marketing site — React SPA with Express contact form backend, deployed on zet.home.arpa behind nginx SSL termination.

Deployment Architecture

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"]
        ddns["DDNS client\nupdates lair.kenjim.com A record\nwhen WAN IP changes"]
        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)"]
    end

    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

www.kenjim.com/
├── docker-compose.yml       # both services run network_mode: host
├── frontend/
│   ├── 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

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

cd ~/workspace/src/personal/www.kenjim.com

docker compose up --build -d   # build images and start
docker compose down            # stop
docker compose logs -f         # watch logs
docker logs www-kenjim-api     # backend only
docker logs www-kenjim-frontend # frontend only

Environment

.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:

# manual renewal (cron handles this automatically)
~/.acme.sh/acme.sh --renew -d kenjim.com --wildcard --dns dns_gd

Cert location: /etc/nginx/ssl/kenjim.com/

Mac Setup (first time)

git clone http://kenjim@git.kenjim.com/kenjim/www.kenjim.com.git
cd www.kenjim.com
# edit frontend/src, then:
git add . && git commit -m "..." && git push