From 3dc9e22dc5e1e719f86aa7d0da084892191e668a Mon Sep 17 00:00:00 2001 From: William Mantly Date: Wed, 31 Dec 2025 17:37:54 -0500 Subject: [PATCH] Fix architecture documentation - clarify Redis-first lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OpenResty checks Redis FIRST for every request - Node.js is only queried as fallback when Redis has no entry - Cached hosts continue working even if Node.js goes down - Updated diagram to show lookup hierarchy - Restructured caching strategy to emphasize L1/L2 tiers πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- docs/architecture.md | 81 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 36f18de2..61f91ba3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -23,33 +23,30 @@ The proxy system consists of three main components working together to provide h β”‚ β”‚ SSL Terminationβ”‚ β”‚ Host Routing β”‚ β”‚ Request Proxyingβ”‚ β”‚ β”‚ β”‚ (lua-resty- β”‚ β”‚ (targetinfo. β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ auto-ssl) β”‚ β”‚ lua) β”‚ β”‚ β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜ β”‚ β”‚ β”‚ - Let's Encrypt Unix Socket Backend - HTTP-01 Lookup Query Services + Let's Encrypt 1. Check Redis FIRST Backend + HTTP-01 2. Unix Socket (fallback) Services β”‚ β”‚ β”‚ β–Ό β–Ό β–Ό -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Node.js Application β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ β”‚ Services β”‚ β”‚ Models β”‚ β”‚ Routes β”‚ β”‚ -β”‚ β”‚ - host_lookupβ”‚ β”‚ - Host β”‚ β”‚ - /api/host β”‚ β”‚ -β”‚ β”‚ - scheduler β”‚ β”‚ - DNS Provider β”‚ β”‚ - /api/dns β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - User β”‚ β”‚ - /api/user β”‚ β”‚ -β”‚ β”‚ - Auth β”‚ β”‚ - /api/auth β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - /api/cert β”‚ β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ - β–Ό β–Ό -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Redis β”‚ β”‚ DNS Providers β”‚ -β”‚ - Host configs β”‚ β”‚ - CloudFlare β”‚ -β”‚ - User accounts β”‚ β”‚ - DigitalOcean β”‚ -β”‚ - SSL certs β”‚ β”‚ - PorkBun β”‚ -β”‚ - Auth tokens β”‚ β”‚ (DNS-01 challenges) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Redis β”‚ β”‚ Node.js Application β”‚ +β”‚ (Primary Cache) β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ - Host configs ◄────┼──┼─── Services β”‚ β”‚ Routes β”‚ β”‚ +β”‚ - User accounts β”‚ β”‚ β”‚ - host_lookupβ”‚ β”‚ - /api/*β”‚ β”‚ +β”‚ - SSL certs β”‚ β”‚ β”‚ - scheduler β”‚ β”‚ β”‚ β”‚ +β”‚ - Auth tokens β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β–Ό + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ DNS Providers β”‚ + β”‚ - CloudFlare β”‚ + β”‚ - DigitalOcean β”‚ + β”‚ - PorkBun β”‚ + β”‚ (DNS-01 challenges) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Component Details @@ -59,14 +56,15 @@ The proxy system consists of three main components working together to provide h **Responsibilities:** - Accept incoming HTTP/HTTPS requests - SSL termination using lua-resty-auto-ssl -- Host-based routing decisions +- Host-based routing decisions (Redis-first lookup) - Proxy requests to backend services **Key Features:** - HTTP-01 ACME challenge handling for automatic SSL -- Lua-based host lookup via Unix socket +- Redis-first host lookup with Node.js fallback via Unix socket - High-performance event-driven architecture - Support for WebSocket connections +- Continues serving cached hosts even if Node.js is down **Configuration Files:** - `/etc/openresty/nginx.conf` - Main configuration @@ -131,13 +129,15 @@ proxy_Domain_ # Domain info 1. **Client** sends HTTPS request to `app.example.com` 2. **OpenResty** receives request, terminates SSL -3. **Lua script** (`targetinfo.lua`) queries Redis for host config -4. If **cache miss**, Lua queries Node.js via Unix socket -5. **Node.js** performs host lookup (supports wildcards) -6. **Response** returned with target IP and port -7. **OpenResty** proxies request to backend service +3. **Lua script** (`targetinfo.lua`) queries **Redis first** for host config +4. If **found in Redis**, jump to step 7 (Node.js not involved) +5. If **not in Redis**, Lua queries Node.js via Unix socket as fallback +6. **Node.js** performs host lookup (supports wildcards), caches result in Redis +7. **OpenResty** proxies request to backend service using target IP and port 8. **Response** proxied back to client +**Resilience**: If Node.js goes down, all hosts already cached in Redis continue to work. Only new/uncached hosts will fail until Node.js recovers. + ### Wildcard SSL Certificate Request 1. **User** creates wildcard host (`*.example.com`) via API @@ -210,10 +210,21 @@ Priority: Exact > Single wildcard (*) > Double wildcard (**) ### Caching Strategy -1. **Redis cache** - Primary host configuration storage -2. **Lookup tree** - In-memory host lookup (rebuilt on changes) -3. **OpenResty cache** - Reduces Unix socket calls -4. **Wildcard parent caching** - Stores resolved wildcard parents +The system uses a multi-tier caching approach: + +1. **Redis (L1 Cache)** - OpenResty checks Redis FIRST for every request + - Primary host configuration storage + - Survives Node.js restarts/failures + - Shared across all OpenResty workers + +2. **Node.js Lookup Tree (L2 Cache)** - In-memory host lookup with wildcard matching + - Only queried when Redis has no entry + - Rebuilt automatically when hosts change + - Supports complex wildcard resolution + +3. **Wildcard Parent Caching** - Resolved wildcard matches stored back to Redis + - Subsequent requests to `api.example.com` hit Redis directly + - No repeated wildcard resolution needed ### Unix Socket vs HTTP API