Update docs + add 3 SVG flowcharts

New diagrams:
- business-flow.svg: acquisition → check → order → filing → delivery
- technical-architecture.svg: full Docker stack, data tier, external services
- order-flow.svg: detailed worker pipeline with eSign gate and handler map

Updated docs:
- infrastructure.md: DocServer, email servers, backup server sections
- architecture.md: linked to new SVGs, updated date

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-07 09:17:36 -05:00
parent c127cdd908
commit 9c1b1387cb
5 changed files with 619 additions and 6 deletions

View file

@ -1,6 +1,11 @@
# System Architecture
**Last updated:** 2026-04-17 (15 Docker containers + k3s SHKeeper pods + Windows DocServer VM + dev stack + crypto treasury + foreign qualification + compliance check tool)
**Last updated:** 2026-05-07 (15 Docker containers + k3s SHKeeper pods + Windows DocServer + Postfix/OpenDKIM + bounce watcher + dev stack)
See also:
- [Business Flow Diagram](business-flow.svg)
- [Technical Architecture Diagram](technical-architecture.svg)
- [Order Processing Flowchart](order-flow.svg)
## Overview

215
docs/business-flow.svg Normal file
View file

@ -0,0 +1,215 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 900" font-family="Inter, system-ui, sans-serif" font-size="11">
<defs>
<marker id="arr" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#64748b"/>
</marker>
<marker id="arr-green" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#059669"/>
</marker>
<marker id="arr-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<path d="M0,0 L8,3 L0,6 Z" fill="#1e3a5f"/>
</marker>
</defs>
<!-- Title -->
<text x="700" y="30" text-anchor="middle" font-size="18" font-weight="700" fill="#1e3a5f">Performance West — Business Flow</text>
<text x="700" y="48" text-anchor="middle" font-size="10" fill="#94a3b8">Customer acquisition → compliance check → order → filing → delivery</text>
<!-- ═══ ACQUISITION CHANNELS ═══ -->
<rect x="20" y="70" width="200" height="160" rx="10" fill="#eff6ff" stroke="#bfdbfe" stroke-width="1.5"/>
<text x="120" y="90" text-anchor="middle" font-size="12" font-weight="700" fill="#1e3a5f">Acquisition Channels</text>
<text x="35" y="112" font-size="10" fill="#475569">📧 Listmonk email campaigns</text>
<text x="35" y="128" font-size="10" fill="#475569">🔍 Google / Bing organic</text>
<text x="35" y="144" font-size="10" fill="#475569">💬 Reddit / forum monitoring</text>
<text x="35" y="160" font-size="10" fill="#475569">📞 Referrals / word of mouth</text>
<text x="35" y="176" font-size="10" fill="#475569">⚖️ Attorney referral program</text>
<text x="35" y="196" font-size="10" fill="#475569">📊 FCC deficiency alerts (2,718)</text>
<text x="35" y="212" font-size="9" fill="#94a3b8">Direct SMTP via Postfix + DKIM</text>
<!-- Arrow: Acquisition → Free Tool -->
<line x1="220" y1="150" x2="280" y2="150" stroke="#64748b" stroke-width="1.5" marker-end="url(#arr)"/>
<!-- ═══ FREE COMPLIANCE CHECK ═══ -->
<rect x="285" y="80" width="220" height="140" rx="10" fill="#dcfce7" stroke="#86efac" stroke-width="1.5"/>
<text x="395" y="100" text-anchor="middle" font-size="12" font-weight="700" fill="#059669">Free Compliance Check</text>
<text x="300" y="120" font-size="10" fill="#374151">performancewest.net/tools/</text>
<text x="300" y="134" font-size="10" fill="#374151">fcc-compliance-check</text>
<text x="300" y="155" font-size="9" fill="#475569">• Enter FRN → auto-run from email</text>
<text x="300" y="168" font-size="9" fill="#475569">• Queries RMD, CPNI, USAC, BDC</text>
<text x="300" y="181" font-size="9" fill="#475569">• Shows issues with severity</text>
<text x="300" y="194" font-size="9" fill="#475569">• CTA: "Fix These Issues"</text>
<text x="300" y="208" font-size="9" fill="#94a3b8">Logged to compliance_check_log</text>
<!-- Arrow: Check → Order -->
<line x1="505" y1="150" x2="565" y2="150" stroke="#059669" stroke-width="1.5" marker-end="url(#arr-green)"/>
<text x="535" y="142" text-anchor="middle" font-size="9" fill="#059669" font-weight="600">Order</text>
<!-- ═══ CHECKOUT ═══ -->
<rect x="570" y="80" width="200" height="140" rx="10" fill="#fef3c7" stroke="#fcd34d" stroke-width="1.5"/>
<text x="670" y="100" text-anchor="middle" font-size="12" font-weight="700" fill="#92400e">Checkout &amp; Payment</text>
<text x="585" y="120" font-size="10" fill="#374151">Batch or single service order</text>
<text x="585" y="140" font-size="9" fill="#475569">💳 Stripe (card, ACH, Klarna)</text>
<text x="585" y="153" font-size="9" fill="#475569">🅿️ PayPal</text>
<text x="585" y="166" font-size="9" fill="#475569">₿ SHKeeper (BTC, ETH, USDC)</text>
<text x="585" y="186" font-size="9" fill="#475569">→ PG compliance_orders</text>
<text x="585" y="199" font-size="9" fill="#475569">→ ERPNext Sales Order</text>
<text x="585" y="212" font-size="9" fill="#475569">→ Telegram notification</text>
<!-- Arrow: Checkout → Worker -->
<line x1="770" y1="150" x2="830" y2="150" stroke="#1e3a5f" stroke-width="1.5" marker-end="url(#arr-blue)"/>
<text x="800" y="142" text-anchor="middle" font-size="9" fill="#1e3a5f" font-weight="600">Dispatch</text>
<!-- ═══ WORKER PROCESSING ═══ -->
<rect x="835" y="70" width="260" height="160" rx="10" fill="#ede9fe" stroke="#c4b5fd" stroke-width="1.5"/>
<text x="965" y="90" text-anchor="middle" font-size="12" font-weight="700" fill="#5b21b6">Worker Processing</text>
<text x="850" y="110" font-size="10" fill="#374151">Python job server (port 8090)</text>
<text x="850" y="128" font-size="9" fill="#475569">1. Generate documents (DOCX→PDF)</text>
<text x="850" y="141" font-size="9" fill="#475569">2. Upload to MinIO</text>
<text x="850" y="154" font-size="9" fill="#475569">3. Send for eSign (portal link)</text>
<text x="850" y="167" font-size="9" fill="#475569">4. Client signs → resume pipeline</text>
<text x="850" y="180" font-size="9" fill="#475569">5. File with FCC/USAC (Playwright)</text>
<text x="850" y="193" font-size="9" fill="#475569">6. Capture confirmation</text>
<text x="850" y="206" font-size="9" fill="#475569">7. Email docs to client</text>
<text x="850" y="222" font-size="9" fill="#94a3b8">AUTO_FILING=false → admin review</text>
<!-- Arrow: Worker → Delivery -->
<line x1="1095" y1="150" x2="1145" y2="150" stroke="#1e3a5f" stroke-width="1.5" marker-end="url(#arr-blue)"/>
<!-- ═══ DELIVERY ═══ -->
<rect x="1150" y="80" width="220" height="140" rx="10" fill="#dbeafe" stroke="#93c5fd" stroke-width="1.5"/>
<text x="1260" y="100" text-anchor="middle" font-size="12" font-weight="700" fill="#1e3a5f">Delivery &amp; Portal</text>
<text x="1165" y="120" font-size="9" fill="#475569">📄 Documents emailed to client</text>
<text x="1165" y="136" font-size="9" fill="#475569">📋 FCC confirmation number</text>
<text x="1165" y="152" font-size="9" fill="#475569">🌐 Portal order tracking</text>
<text x="1165" y="168" font-size="9" fill="#475569">📊 ERPNext Sales Invoice (paid)</text>
<text x="1165" y="184" font-size="9" fill="#475569">📅 Compliance calendar created</text>
<text x="1165" y="200" font-size="9" fill="#475569">🔄 Annual renewal auto-scheduled</text>
<!-- ═══ SERVICE CATALOG ═══ -->
<rect x="20" y="270" width="1350" height="30" rx="6" fill="#1e3a5f"/>
<text x="700" y="290" text-anchor="middle" font-size="12" font-weight="700" fill="#fff">Service Catalog — 20 Telecom Services + Corporate + Privacy + TCPA</text>
<!-- Service boxes row 1 -->
<rect x="20" y="310" width="155" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="97" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">FCC Carrier Reg</text>
<text x="97" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#059669">$1,299</text>
<text x="97" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Bundle: 6 services</text>
<rect x="185" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="242" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">499-A Filing</text>
<text x="242" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$499</text>
<text x="242" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Annual revenue</text>
<rect x="310" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="367" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">CPNI Cert</text>
<text x="367" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$199</text>
<text x="367" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Annual cert</text>
<rect x="435" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="492" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">RMD Filing</text>
<text x="492" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$249</text>
<text x="492" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">+$100 FCC fee</text>
<rect x="560" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="617" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">CALEA SSI</text>
<text x="617" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$799</text>
<text x="617" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">SSI plan</text>
<rect x="685" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="742" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">BDC Filing</text>
<text x="742" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$199-349</text>
<text x="742" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Voice / BB / both</text>
<rect x="810" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="867" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">STIR/SHAKEN</text>
<text x="867" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$499</text>
<text x="867" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Implementation</text>
<rect x="935" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="992" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">State PUC</text>
<text x="992" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$399</text>
<text x="992" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Per state + fees</text>
<rect x="1060" y="310" width="115" height="70" rx="6" fill="#fff" stroke="#e2e8f0"/>
<text x="1117" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">NECA OCN</text>
<text x="1117" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">$2,650</text>
<text x="1117" y="360" text-anchor="middle" font-size="8" fill="#94a3b8">Incl. NECA fees</text>
<rect x="1185" y="310" width="185" height="70" rx="6" fill="#dcfce7" stroke="#86efac"/>
<text x="1277" y="330" text-anchor="middle" font-size="10" font-weight="600" fill="#059669">Compliance Check</text>
<text x="1277" y="345" text-anchor="middle" font-size="16" font-weight="700" fill="#059669">FREE</text>
<text x="1277" y="360" text-anchor="middle" font-size="8" fill="#059669">Lead generation tool</text>
<!-- ═══ eSIGN FLOW ═══ -->
<rect x="20" y="410" width="660" height="130" rx="10" fill="#fef2f2" stroke="#fca5a5" stroke-width="1.5"/>
<text x="350" y="430" text-anchor="middle" font-size="12" font-weight="700" fill="#991b1b">eSign Portal — Officer Signature Required Before Filing</text>
<rect x="35" y="445" width="120" height="40" rx="6" fill="#fff" stroke="#fca5a5"/>
<text x="95" y="462" text-anchor="middle" font-size="9" fill="#374151">Handler generates</text>
<text x="95" y="474" text-anchor="middle" font-size="9" fill="#374151">document</text>
<line x1="155" y1="465" x2="175" y2="465" stroke="#991b1b" stroke-width="1" marker-end="url(#arr)"/>
<rect x="180" y="445" width="110" height="40" rx="6" fill="#fff" stroke="#fca5a5"/>
<text x="235" y="462" text-anchor="middle" font-size="9" fill="#374151">Upload PDF to</text>
<text x="235" y="474" text-anchor="middle" font-size="9" fill="#374151">MinIO</text>
<line x1="290" y1="465" x2="310" y2="465" stroke="#991b1b" stroke-width="1" marker-end="url(#arr)"/>
<rect x="315" y="445" width="110" height="40" rx="6" fill="#fff" stroke="#fca5a5"/>
<text x="370" y="462" text-anchor="middle" font-size="9" fill="#374151">Email JWT link</text>
<text x="370" y="474" text-anchor="middle" font-size="9" fill="#374151">to officer</text>
<line x1="425" y1="465" x2="445" y2="465" stroke="#991b1b" stroke-width="1" marker-end="url(#arr)"/>
<rect x="450" y="445" width="110" height="40" rx="6" fill="#fef2f2" stroke="#f87171"/>
<text x="505" y="462" text-anchor="middle" font-size="9" font-weight="600" fill="#991b1b">Client draws/</text>
<text x="505" y="474" text-anchor="middle" font-size="9" font-weight="600" fill="#991b1b">types signature</text>
<line x1="560" y1="465" x2="580" y2="465" stroke="#991b1b" stroke-width="1" marker-end="url(#arr)"/>
<rect x="585" y="445" width="80" height="40" rx="6" fill="#dcfce7" stroke="#86efac"/>
<text x="625" y="462" text-anchor="middle" font-size="9" font-weight="600" fill="#059669">Resume</text>
<text x="625" y="474" text-anchor="middle" font-size="9" font-weight="600" fill="#059669">pipeline</text>
<text x="350" y="525" text-anchor="middle" font-size="9" fill="#94a3b8">Applies to: RMD (perjury), CPNI, CALEA SSI, 499-A engagement letter, discontinuance</text>
<!-- ═══ EMAIL INFRASTRUCTURE ═══ -->
<rect x="700" y="410" width="670" height="130" rx="10" fill="#f0fdf4" stroke="#86efac" stroke-width="1.5"/>
<text x="1035" y="430" text-anchor="middle" font-size="12" font-weight="700" fill="#059669">Email Infrastructure — Direct SMTP</text>
<text x="715" y="452" font-size="9" fill="#374151" font-weight="600">Campaign sending:</text>
<text x="715" y="465" font-size="9" fill="#475569">Listmonk → Postfix (207.174.124.71) → DKIM signed → direct delivery</text>
<text x="715" y="478" font-size="9" fill="#475569">200/day warmup → 2,718 FCC deficiency subscribers on List 9</text>
<text x="715" y="498" font-size="9" fill="#374151" font-weight="600">Transactional email:</text>
<text x="715" y="511" font-size="9" fill="#475569">Carbonio (co.carrierone.com) — order confirmations, intake links, eSign links</text>
<text x="715" y="530" font-size="9" fill="#374151" font-weight="600">Auth: SPF (-all) + DKIM (2048-bit) + DMARC (quarantine) + PTR (FCrDNS)</text>
<!-- ═══ ANALYTICS ═══ -->
<rect x="20" y="560" width="450" height="110" rx="10" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="245" y="580" text-anchor="middle" font-size="12" font-weight="700" fill="#1e3a5f">Analytics &amp; Tracking</text>
<text x="35" y="600" font-size="9" fill="#475569">📊 Umami (analytics.performancewest.net) — page views, events, funnels</text>
<text x="35" y="615" font-size="9" fill="#475569">🎯 Custom events: compliance-check-start/complete, order-cta-click, checkout-start</text>
<text x="35" y="630" font-size="9" fill="#475569">📈 Server-side: payment-complete event, compliance_check_log table</text>
<text x="35" y="645" font-size="9" fill="#475569">📧 Campaign tracking: opens (TrackView pixel), clicks (auto link wrapping)</text>
<text x="35" y="660" font-size="9" fill="#475569">🔄 UTM attribution: listmonk campaigns → compliance checker → checkout</text>
<!-- ═══ MONITORING ═══ -->
<rect x="490" y="560" width="420" height="110" rx="10" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="700" y="580" text-anchor="middle" font-size="12" font-weight="700" fill="#1e3a5f">Monitoring &amp; Alerts</text>
<text x="505" y="600" font-size="9" fill="#475569">📡 Prometheus + Grafana — 10 service probes, container health</text>
<text x="505" y="615" font-size="9" fill="#475569">🔔 Alertmanager → Telegram (critical 1h, warning 6h repeat)</text>
<text x="505" y="630" font-size="9" fill="#475569">🤖 Playwright selector health checks (daily cron)</text>
<text x="505" y="645" font-size="9" fill="#475569">📨 Bounce watcher daemon → Listmonk webhook (blocklist after 2)</text>
<text x="505" y="660" font-size="9" fill="#475569">🔐 Nightly container security updates (base image patches)</text>
<!-- ═══ BACKUP ═══ -->
<rect x="930" y="560" width="440" height="110" rx="10" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1.5"/>
<text x="1150" y="580" text-anchor="middle" font-size="12" font-weight="700" fill="#1e3a5f">Backup &amp; Data</text>
<text x="945" y="600" font-size="9" fill="#475569">🗄️ PostgreSQL: 4x/day dumps → MinIO → off-site rsync</text>
<text x="945" y="615" font-size="9" fill="#475569">🗄️ MariaDB (ERPNext): daily dump → off-site</text>
<text x="945" y="630" font-size="9" fill="#475569">📦 MinIO mirror: daily bucket sync</text>
<text x="945" y="645" font-size="9" fill="#475569">📝 Forgejo: daily repo dump</text>
<text x="945" y="660" font-size="9" fill="#475569">💾 Off-site: rsync to appbackups LXC (207.174.124.50)</text>
<!-- Footer -->
<text x="700" y="700" text-anchor="middle" font-size="9" fill="#94a3b8">Performance West Inc. — Cheyenne, WY — Updated 2026-05-07</text>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,6 +1,6 @@
# Infrastructure
**Last updated:** 2026-04-06
**Last updated:** 2026-05-07
## Production Server — Linux VM
@ -14,7 +14,7 @@
| Disk | 232 GB SSD |
| Network | Bridged, static IP |
## Proxmox VM — Windows (DocServer) — NOT YET PROVISIONED
## Windows DocServer VM
| Resource | Spec |
|----------|------|
@ -22,10 +22,29 @@
| vCPU | 2 |
| RAM | 4 GB |
| Disk | 40 GB SSD |
| Software | Microsoft Office 2021 |
| Service | DocServer on port 5050 |
| Software | Microsoft Office 2021 + Python 3.12 |
| Service | docserver_worker.py (polls MinIO, converts via Word COM) |
The Windows VM will provide high-fidelity DOCX-to-PDF conversion via Office 2021. DocServer exposes a REST API on port 5050. LibreOffice on the Linux VM serves as a fallback.
Pixel-perfect DOCX→PDF conversion via Microsoft Word. Worker polls MinIO `to-convert/` bucket, converts via Word COM, uploads PDF to `converted/`. No HTTP server needed — MinIO is the transport. Requires RDP login after reboot (Word COM needs interactive session). LibreOffice headless is the automatic fallback.
## Email Servers
| IP | Hostname | PTR | Role |
|----|----------|-----|------|
| 207.174.124.15 | co.carrierone.com | co.carrierone.com | Carbonio — transactional email, mailboxes |
| 207.174.124.22 | cp.carrierone.com | cp.carrierone.com | HestiaCP — DNS, Exim4 MTA, .ca domain provisioning |
| 207.174.124.71 | perfwest.performancewest.net | perfwest.performancewest.net | Postfix + OpenDKIM — Listmonk campaign sending |
All three have DKIM (2048-bit RSA), SPF (`-all` hard fail), and DMARC (`p=quarantine`).
## Backup Server
| Resource | Spec |
|----------|------|
| IP | 207.174.124.50 |
| Type | LXC container |
| Role | Off-site backup destination |
| Schedule | PG 4x/day, MariaDB daily, MinIO daily, Forgejo daily |
## External Infrastructure Dependencies

166
docs/order-flow.svg Normal file
View file

@ -0,0 +1,166 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 750" font-family="Inter, system-ui, sans-serif" font-size="10">
<defs>
<marker id="a" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto">
<path d="M0,0 L7,2.5 L0,5 Z" fill="#64748b"/>
</marker>
<marker id="ag" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto">
<path d="M0,0 L7,2.5 L0,5 Z" fill="#059669"/>
</marker>
<marker id="ar" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto">
<path d="M0,0 L7,2.5 L0,5 Z" fill="#dc2626"/>
</marker>
</defs>
<text x="600" y="25" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">FCC Compliance Order — Processing Flow</text>
<!-- Step 1: Email -->
<rect x="30" y="50" width="150" height="50" rx="8" fill="#dbeafe" stroke="#93c5fd"/>
<text x="105" y="72" text-anchor="middle" font-size="10" font-weight="600" fill="#1e3a5f">Email Campaign</text>
<text x="105" y="85" text-anchor="middle" font-size="8" fill="#64748b">Listmonk → Postfix</text>
<line x1="180" y1="75" x2="210" y2="75" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step 2: Compliance Check -->
<rect x="215" y="50" width="150" height="50" rx="8" fill="#dcfce7" stroke="#86efac"/>
<text x="290" y="72" text-anchor="middle" font-size="10" font-weight="600" fill="#059669">Free Check</text>
<text x="290" y="85" text-anchor="middle" font-size="8" fill="#64748b">?frn= auto-run</text>
<line x1="365" y1="75" x2="395" y2="75" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step 3: Results + CTA -->
<rect x="400" y="50" width="150" height="50" rx="8" fill="#fef3c7" stroke="#fcd34d"/>
<text x="475" y="72" text-anchor="middle" font-size="10" font-weight="600" fill="#92400e">Results + CTA</text>
<text x="475" y="85" text-anchor="middle" font-size="8" fill="#64748b">"Fix N Issues"</text>
<line x1="550" y1="75" x2="580" y2="75" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step 4: Checkout -->
<rect x="585" y="50" width="150" height="50" rx="8" fill="#fef3c7" stroke="#fcd34d"/>
<text x="660" y="72" text-anchor="middle" font-size="10" font-weight="600" fill="#92400e">Stripe Checkout</text>
<text x="660" y="85" text-anchor="middle" font-size="8" fill="#64748b">Card / ACH / PayPal</text>
<line x1="735" y1="75" x2="765" y2="75" stroke="#059669" marker-end="url(#ag)"/>
<text x="750" y="68" font-size="8" fill="#059669" font-weight="600">paid</text>
<!-- Step 5: Order Created -->
<rect x="770" y="50" width="160" height="50" rx="8" fill="#1e3a5f"/>
<text x="850" y="72" text-anchor="middle" font-size="10" font-weight="600" fill="#fff">Order Created</text>
<text x="850" y="85" text-anchor="middle" font-size="8" fill="#94a3b8">PG + ERPNext SO</text>
<!-- Arrow down to worker dispatch -->
<line x1="850" y1="100" x2="850" y2="130" stroke="#64748b" marker-end="url(#a)"/>
<text x="870" y="120" font-size="8" fill="#64748b">dispatch</text>
<!-- ═══ WORKER PROCESSING DETAIL ═══ -->
<rect x="30" y="135" width="1140" height="580" rx="10" fill="#faf5ff" stroke="#c4b5fd" stroke-width="1.5"/>
<text x="600" y="155" text-anchor="middle" font-size="12" font-weight="700" fill="#5b21b6">Worker Processing Pipeline (Python job server)</text>
<!-- Step A: Generate Docs -->
<rect x="50" y="170" width="250" height="80" rx="8" fill="#fff" stroke="#c4b5fd"/>
<text x="175" y="190" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">1. Generate Documents</text>
<text x="60" y="208" font-size="9" fill="#475569">• Template selected by carrier type</text>
<text x="60" y="221" font-size="9" fill="#475569">• DOCX generated (python-docx)</text>
<text x="60" y="234" font-size="9" fill="#475569">• Convert to PDF (Word VM / LibreOffice)</text>
<text x="60" y="245" font-size="8" fill="#94a3b8">_styles.py shared across 26 generators</text>
<line x1="300" y1="210" x2="340" y2="210" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step B: Upload MinIO -->
<rect x="345" y="170" width="180" height="80" rx="8" fill="#fff" stroke="#c4b5fd"/>
<text x="435" y="190" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">2. Upload to MinIO</text>
<text x="355" y="208" font-size="9" fill="#475569">• PDF stored at</text>
<text x="355" y="221" font-size="9" fill="#475569"> compliance/{order}/</text>
<text x="355" y="234" font-size="9" fill="#475569">• Presigned URL for portal</text>
<line x1="525" y1="210" x2="565" y2="210" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step C: eSign Gate -->
<rect x="570" y="170" width="200" height="80" rx="8" fill="#fef2f2" stroke="#fca5a5"/>
<text x="670" y="190" text-anchor="middle" font-size="10" font-weight="700" fill="#dc2626">3. eSign Gate</text>
<text x="580" y="208" font-size="9" fill="#475569">• Insert esign_records row</text>
<text x="580" y="221" font-size="9" fill="#475569">• Email JWT signing link</text>
<text x="580" y="234" font-size="9" fill="#475569">• PAUSE — wait for signature</text>
<text x="580" y="245" font-size="8" fill="#dc2626" font-weight="600">Handler returns [] (no delivery yet)</text>
<!-- Arrow down: client signs -->
<line x1="670" y1="250" x2="670" y2="280" stroke="#dc2626" marker-end="url(#ar)"/>
<text x="690" y="270" font-size="8" fill="#dc2626" font-weight="600">client signs</text>
<!-- Step C2: Signature received -->
<rect x="570" y="285" width="200" height="55" rx="8" fill="#dcfce7" stroke="#86efac"/>
<text x="670" y="305" text-anchor="middle" font-size="10" font-weight="700" fill="#059669">3b. Signature Received</text>
<text x="580" y="322" font-size="9" fill="#475569">esign_completed → re-dispatch</text>
<text x="580" y="335" font-size="9" fill="#475569">handler with client_approved=true</text>
<line x1="770" y1="312" x2="810" y2="312" stroke="#059669" marker-end="url(#ag)"/>
<!-- Step D: Auto-filing check -->
<rect x="815" y="170" width="200" height="80" rx="8" fill="#fff" stroke="#c4b5fd"/>
<text x="915" y="190" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">4. Auto-Filing Check</text>
<text x="825" y="208" font-size="9" fill="#475569">AUTO_FILING_ENABLED=true?</text>
<text x="825" y="228" font-size="9" fill="#059669" font-weight="600">YES → proceed to filing</text>
<text x="825" y="241" font-size="9" fill="#dc2626" font-weight="600">NO → admin review todo</text>
<line x1="915" y1="250" x2="915" y2="280" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step E: File with FCC -->
<rect x="815" y="285" width="200" height="80" rx="8" fill="#fff" stroke="#c4b5fd"/>
<text x="915" y="305" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">5. File with FCC/USAC</text>
<text x="825" y="323" font-size="9" fill="#475569">• Playwright browser automation</text>
<text x="825" y="336" font-size="9" fill="#475569">• ECFS, RMD portal, USAC E-File</text>
<text x="825" y="349" font-size="9" fill="#475569">• Screenshot confirmation capture</text>
<text x="825" y="360" font-size="8" fill="#94a3b8">Failure → Telegram alert + admin todo</text>
<line x1="915" y1="365" x2="915" y2="395" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step F: Record + Deliver -->
<rect x="815" y="400" width="200" height="80" rx="8" fill="#fff" stroke="#c4b5fd"/>
<text x="915" y="420" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">6. Record &amp; Deliver</text>
<text x="825" y="438" font-size="9" fill="#475569">• filing_state recorded in PG</text>
<text x="825" y="451" font-size="9" fill="#475569">• ERPNext SO → "Filed" workflow</text>
<text x="825" y="464" font-size="9" fill="#475569">• Confirmation email to client</text>
<text x="825" y="475" font-size="8" fill="#94a3b8">Compliance Deadline DocType created</text>
<line x1="815" y1="440" x2="540" y2="440" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step G: Client delivery -->
<rect x="340" y="400" width="195" height="80" rx="8" fill="#dcfce7" stroke="#86efac"/>
<text x="437" y="420" text-anchor="middle" font-size="10" font-weight="700" fill="#059669">7. Client Delivery</text>
<text x="350" y="438" font-size="9" fill="#475569">• Documents emailed (PDF)</text>
<text x="350" y="451" font-size="9" fill="#475569">• Confirmation # in email</text>
<text x="350" y="464" font-size="9" fill="#475569">• Portal shows "Filed" status</text>
<text x="350" y="475" font-size="8" fill="#94a3b8">Invoice marked Paid in ERPNext</text>
<line x1="340" y1="440" x2="300" y2="440" stroke="#64748b" marker-end="url(#a)"/>
<!-- Step H: Renewal -->
<rect x="50" y="400" width="245" height="80" rx="8" fill="#dbeafe" stroke="#93c5fd"/>
<text x="172" y="420" text-anchor="middle" font-size="10" font-weight="700" fill="#1e3a5f">8. Renewal Cycle</text>
<text x="60" y="438" font-size="9" fill="#475569">• Compliance Calendar entry created</text>
<text x="60" y="451" font-size="9" fill="#475569">• Reminder emails at 30/14/7 days</text>
<text x="60" y="464" font-size="9" fill="#475569">• Auto-invoice via renewal_worker</text>
<text x="60" y="475" font-size="8" fill="#94a3b8">Annual: RMD Mar 1, CPNI Mar 1, 499-A Oct 1</text>
<!-- ═══ SERVICE HANDLER MAP ═══ -->
<rect x="50" y="500" width="1110" height="90" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="605" y="520" text-anchor="middle" font-size="10" font-weight="700" fill="#1e3a5f">Service Handlers (scripts/workers/services/)</text>
<text x="60" y="540" font-size="8" fill="#475569" font-weight="600">Playwright auto-filing:</text>
<text x="60" y="552" font-size="8" fill="#475569">rmd_filing, cpni_certification, form_499a, form_499q, cores_frn_registration, bdc_filing, foreign_carrier_affiliation, form_499_initial</text>
<text x="60" y="570" font-size="8" fill="#475569" font-weight="600">Admin-driven (todo-based):</text>
<text x="60" y="582" font-size="8" fill="#475569">fcc_carrier_registration (8-step), state_puc, ocn_registration, calea_ssi, form_499a_discontinuance, dc_agent, cdr_analysis, canada_crtc (14-step)</text>
<!-- ═══ eSIGN SERVICES ═══ -->
<rect x="50" y="600" width="1110" height="55" rx="8" fill="#fef2f2" stroke="#fca5a5"/>
<text x="605" y="618" text-anchor="middle" font-size="10" font-weight="700" fill="#dc2626">eSign Required Before Filing</text>
<text x="80" y="638" font-size="9" fill="#475569">RMD (perjury 47 CFR § 1.16) | CPNI (officer attestation) | CALEA SSI (plan signature) | 499-A engagement letter | Discontinuance (officer signs deactivation letter)</text>
<text x="80" y="650" font-size="8" fill="#94a3b8">Generic portal: /portal/esign/ | esign_records table | JWT 72h tokens | Draw or type signature | Webhook resumes pipeline</text>
<!-- ═══ DATA FLOW KEY ═══ -->
<rect x="50" y="665" width="1110" height="40" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
<text x="100" y="685" font-size="9" fill="#374151" font-weight="600">Data stores:</text>
<text x="185" y="685" font-size="8" fill="#475569">PG compliance_orders → ERPNext Sales Order → ERPNext Sales Invoice → ERPNext Payment Entry</text>
<text x="700" y="685" font-size="9" fill="#374151" font-weight="600">Documents:</text>
<text x="778" y="685" font-size="8" fill="#475569">MinIO compliance/{order}/ → eSign portal → Postfix email delivery</text>
<text x="100" y="698" font-size="9" fill="#374151" font-weight="600">Notifications:</text>
<text x="195" y="698" font-size="8" fill="#475569">Telegram (new order + filing failures) | Email (intake, eSign, confirmation, renewal reminders)</text>
<text x="600" y="735" text-anchor="middle" font-size="9" fill="#94a3b8">Performance West Inc. — Updated 2026-05-07</text>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,208 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 950" font-family="Inter, system-ui, sans-serif" font-size="10">
<defs>
<marker id="a" markerWidth="7" markerHeight="5" refX="7" refY="2.5" orient="auto">
<path d="M0,0 L7,2.5 L0,5 Z" fill="#94a3b8"/>
</marker>
</defs>
<text x="700" y="25" text-anchor="middle" font-size="16" font-weight="700" fill="#1e3a5f">Performance West — Technical Architecture</text>
<text x="700" y="42" text-anchor="middle" font-size="9" fill="#94a3b8">207.174.124.71 (Debian 13) — Docker Compose multi-service stack — Updated 2026-05-07</text>
<!-- ═══ INTERNET / CLIENT LAYER ═══ -->
<rect x="20" y="55" width="1360" height="50" rx="8" fill="#f0f4f8" stroke="#cbd5e1"/>
<text x="700" y="75" text-anchor="middle" font-size="11" font-weight="700" fill="#475569">Internet — Clients &amp; External APIs</text>
<text x="120" y="93" text-anchor="middle" font-size="9" fill="#64748b">Browsers</text>
<text x="340" y="93" text-anchor="middle" font-size="9" fill="#64748b">Email recipients</text>
<text x="560" y="93" text-anchor="middle" font-size="9" fill="#64748b">Stripe / PayPal</text>
<text x="780" y="93" text-anchor="middle" font-size="9" fill="#64748b">FCC CORES / ECFS / USAC</text>
<text x="1020" y="93" text-anchor="middle" font-size="9" fill="#64748b">ERPNext portal users</text>
<text x="1260" y="93" text-anchor="middle" font-size="9" fill="#64748b">Umami / Grafana</text>
<!-- Arrows down -->
<line x1="120" y1="105" x2="120" y2="130" stroke="#94a3b8" stroke-width="1" marker-end="url(#a)"/>
<line x1="560" y1="105" x2="560" y2="130" stroke="#94a3b8" stroke-width="1" marker-end="url(#a)"/>
<line x1="1020" y1="105" x2="1020" y2="130" stroke="#94a3b8" stroke-width="1" marker-end="url(#a)"/>
<!-- ═══ NGINX REVERSE PROXY ═══ -->
<rect x="20" y="130" width="1360" height="40" rx="8" fill="#1e3a5f"/>
<text x="700" y="155" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">nginx Reverse Proxy — TLS termination, sub_filter branding, rate limiting</text>
<text x="120" y="165" font-size="8" fill="#94a3b8">:443 → site:4322</text>
<text x="340" y="165" font-size="8" fill="#94a3b8">:443 → api:3001</text>
<text x="560" y="165" font-size="8" fill="#94a3b8">portal → erpnext:8000</text>
<text x="780" y="165" font-size="8" fill="#94a3b8">lists → listmonk:9100</text>
<text x="1000" y="165" font-size="8" fill="#94a3b8">analytics → umami:3100</text>
<text x="1200" y="165" font-size="8" fill="#94a3b8">grafana:3000</text>
<!-- ═══ APPLICATION TIER ═══ -->
<text x="30" y="195" font-size="11" font-weight="700" fill="#1e3a5f">Application Tier</text>
<!-- Site container -->
<rect x="20" y="205" width="200" height="90" rx="8" fill="#dbeafe" stroke="#93c5fd"/>
<text x="120" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#1e3a5f">site (Astro → nginx)</text>
<text x="30" y="238" font-size="9" fill="#475569">Static site + public HTML</text>
<text x="30" y="251" font-size="9" fill="#475569">Compliance checker, order pages</text>
<text x="30" y="264" font-size="9" fill="#475569">eSign portal, pricing</text>
<text x="30" y="277" font-size="8" fill="#94a3b8">Port 4322 | nginx:alpine</text>
<!-- API container -->
<rect x="235" y="205" width="200" height="90" rx="8" fill="#dbeafe" stroke="#93c5fd"/>
<text x="335" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#1e3a5f">api (Express.js)</text>
<text x="245" y="238" font-size="9" fill="#475569">REST API, checkout, webhooks</text>
<text x="245" y="251" font-size="9" fill="#475569">FCC lookup, compliance orders</text>
<text x="245" y="264" font-size="9" fill="#475569">Portal auth (JWT 72h)</text>
<text x="245" y="277" font-size="8" fill="#94a3b8">Port 3001 | node:22-slim</text>
<!-- Workers container -->
<rect x="450" y="205" width="220" height="90" rx="8" fill="#ede9fe" stroke="#c4b5fd"/>
<text x="560" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">workers (Python)</text>
<text x="460" y="238" font-size="9" fill="#475569">Job server, 27 service handlers</text>
<text x="460" y="251" font-size="9" fill="#475569">Playwright FCC/USAC automation</text>
<text x="460" y="264" font-size="9" fill="#475569">DOCX gen → PDF (Word VM / LO)</text>
<text x="460" y="277" font-size="8" fill="#94a3b8">Port 8090 | python:3.12-slim</text>
<!-- ERPNext -->
<rect x="685" y="205" width="200" height="90" rx="8" fill="#fef3c7" stroke="#fcd34d"/>
<text x="785" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#92400e">erpnext (Frappe)</text>
<text x="695" y="238" font-size="9" fill="#475569">CRM, Sales Orders, Invoices</text>
<text x="695" y="251" font-size="9" fill="#475569">Customer portal (/orders)</text>
<text x="695" y="264" font-size="9" fill="#475569">Custom: PW apps + payments</text>
<text x="695" y="277" font-size="8" fill="#94a3b8">Port 8080 | frappe/erpnext:v15</text>
<!-- Listmonk -->
<rect x="900" y="205" width="170" height="90" rx="8" fill="#dcfce7" stroke="#86efac"/>
<text x="985" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#059669">listmonk</text>
<text x="910" y="238" font-size="9" fill="#475569">Email campaigns (110+)</text>
<text x="910" y="251" font-size="9" fill="#475569">Subscriber lists (9)</text>
<text x="910" y="264" font-size="9" fill="#475569">Bounce webhook API</text>
<text x="910" y="277" font-size="8" fill="#94a3b8">Port 9100 | listmonk:latest</text>
<!-- Monitoring stack -->
<rect x="1085" y="205" width="295" height="90" rx="8" fill="#fef2f2" stroke="#fca5a5"/>
<text x="1232" y="222" text-anchor="middle" font-size="10" font-weight="700" fill="#991b1b">Monitoring Stack</text>
<text x="1095" y="238" font-size="9" fill="#475569">Prometheus + Alertmanager → Telegram</text>
<text x="1095" y="251" font-size="9" fill="#475569">Grafana dashboards (3000)</text>
<text x="1095" y="264" font-size="9" fill="#475569">Umami analytics (3100)</text>
<text x="1095" y="277" font-size="8" fill="#94a3b8">cAdvisor, node-exporter, blackbox</text>
<!-- ═══ DATA TIER ═══ -->
<text x="30" y="320" font-size="11" font-weight="700" fill="#1e3a5f">Data Tier</text>
<!-- PostgreSQL -->
<rect x="20" y="330" width="200" height="75" rx="8" fill="#e0f2fe" stroke="#7dd3fc"/>
<text x="120" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#0369a1">PostgreSQL</text>
<text x="30" y="363" font-size="9" fill="#475569">compliance_orders, telecom_entities</text>
<text x="30" y="376" font-size="9" fill="#475569">esign_records, check_log</text>
<text x="30" y="389" font-size="9" fill="#475569">77 migrations applied</text>
<text x="30" y="400" font-size="8" fill="#94a3b8">Port 5432 | 4x/day backup</text>
<!-- MariaDB -->
<rect x="235" y="330" width="200" height="75" rx="8" fill="#e0f2fe" stroke="#7dd3fc"/>
<text x="335" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#0369a1">MariaDB (ERPNext)</text>
<text x="245" y="363" font-size="9" fill="#475569">Sales Orders, Invoices, Customers</text>
<text x="245" y="376" font-size="9" fill="#475569">Compliance Calendar/Deadline</text>
<text x="245" y="389" font-size="9" fill="#475569">7 custom DocTypes</text>
<text x="245" y="400" font-size="8" fill="#94a3b8">Port 3306 | daily backup</text>
<!-- MinIO -->
<rect x="450" y="330" width="200" height="75" rx="8" fill="#e0f2fe" stroke="#7dd3fc"/>
<text x="550" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#0369a1">MinIO (S3)</text>
<text x="460" y="363" font-size="9" fill="#475569">Document storage (DOCX, PDF)</text>
<text x="460" y="376" font-size="9" fill="#475569">Compliance packets, binders</text>
<text x="460" y="389" font-size="9" fill="#475569">DocServer DOCX↔PDF queue</text>
<text x="460" y="400" font-size="8" fill="#94a3b8">Port 9000/9001 | daily mirror</text>
<!-- Redis -->
<rect x="665" y="330" width="130" height="75" rx="8" fill="#e0f2fe" stroke="#7dd3fc"/>
<text x="730" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#0369a1">Redis</text>
<text x="675" y="363" font-size="9" fill="#475569">ERPNext cache</text>
<text x="675" y="376" font-size="9" fill="#475569">Queue, SocketIO</text>
<text x="675" y="400" font-size="8" fill="#94a3b8">Port 6379</text>
<!-- Ollama -->
<rect x="810" y="330" width="130" height="75" rx="8" fill="#f5f3ff" stroke="#c4b5fd"/>
<text x="875" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#5b21b6">Ollama (LLM)</text>
<text x="820" y="363" font-size="9" fill="#475569">qwen2.5:7b</text>
<text x="820" y="376" font-size="9" fill="#475569">Doc generation</text>
<text x="820" y="400" font-size="8" fill="#94a3b8">Port 11434</text>
<!-- Forgejo -->
<rect x="955" y="330" width="130" height="75" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
<text x="1020" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">Forgejo (Git)</text>
<text x="965" y="363" font-size="9" fill="#475569">git.performancewest.net</text>
<text x="965" y="376" font-size="9" fill="#475569">Source code repo</text>
<text x="965" y="400" font-size="8" fill="#94a3b8">Port 2222 (SSH) / 3033</text>
<!-- k3s / SHKeeper -->
<rect x="1100" y="330" width="130" height="75" rx="8" fill="#fef3c7" stroke="#fcd34d"/>
<text x="1165" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#92400e">k3s (SHKeeper)</text>
<text x="1110" y="363" font-size="9" fill="#475569">Crypto payments</text>
<text x="1110" y="376" font-size="9" fill="#475569">BTC, ETH, USDC</text>
<text x="1110" y="400" font-size="8" fill="#94a3b8">crypto.performancewest.net</text>
<!-- Umami PG -->
<rect x="1245" y="330" width="130" height="75" rx="8" fill="#e0f2fe" stroke="#7dd3fc"/>
<text x="1310" y="347" text-anchor="middle" font-size="10" font-weight="700" fill="#0369a1">Umami PG</text>
<text x="1255" y="363" font-size="9" fill="#475569">Analytics events</text>
<text x="1255" y="376" font-size="9" fill="#475569">Sessions, page views</text>
<text x="1255" y="400" font-size="8" fill="#94a3b8">Internal only</text>
<!-- ═══ EXTERNAL SERVICES ═══ -->
<text x="30" y="435" font-size="11" font-weight="700" fill="#1e3a5f">External Services &amp; Servers</text>
<rect x="20" y="445" width="190" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="115" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">Carbonio Mail</text>
<text x="30" y="478" font-size="9" fill="#475569">207.174.124.15</text>
<text x="30" y="491" font-size="9" fill="#475569">co.carrierone.com</text>
<text x="30" y="504" font-size="9" fill="#475569">DKIM + transactional email</text>
<text x="30" y="517" font-size="8" fill="#94a3b8">PTR: co.carrierone.com</text>
<rect x="225" y="445" width="190" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="320" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">HestiaCP</text>
<text x="235" y="478" font-size="9" fill="#475569">207.174.124.22</text>
<text x="235" y="491" font-size="9" fill="#475569">cp.carrierone.com</text>
<text x="235" y="504" font-size="9" fill="#475569">DNS, Exim4 MTA, .ca domains</text>
<text x="235" y="517" font-size="8" fill="#94a3b8">PTR: cp.carrierone.com</text>
<rect x="430" y="445" width="190" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="525" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">Postfix (local)</text>
<text x="440" y="478" font-size="9" fill="#475569">207.174.124.71</text>
<text x="440" y="491" font-size="9" fill="#475569">Campaign SMTP + OpenDKIM</text>
<text x="440" y="504" font-size="9" fill="#475569">Bounce watcher daemon</text>
<text x="440" y="517" font-size="8" fill="#94a3b8">PTR: perfwest.performancewest.net</text>
<rect x="635" y="445" width="170" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="720" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">Windows DocServer</text>
<text x="645" y="478" font-size="9" fill="#475569">Word COM → PDF</text>
<text x="645" y="491" font-size="9" fill="#475569">Polls MinIO to-convert/</text>
<text x="645" y="504" font-size="9" fill="#475569">Fallback: LibreOffice</text>
<text x="645" y="517" font-size="8" fill="#94a3b8">Requires RDP login after reboot</text>
<rect x="820" y="445" width="170" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="905" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">Backup Server</text>
<text x="830" y="478" font-size="9" fill="#475569">207.174.124.50</text>
<text x="830" y="491" font-size="9" fill="#475569">appbackups LXC</text>
<text x="830" y="504" font-size="9" fill="#475569">PG, MariaDB, MinIO, Forgejo</text>
<text x="830" y="517" font-size="8" fill="#94a3b8">Off-site rsync daily</text>
<rect x="1005" y="445" width="190" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="1100" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">DNS (HE + HestiaCP)</text>
<text x="1015" y="478" font-size="9" fill="#475569">ns0.cp.carrierone.com</text>
<text x="1015" y="491" font-size="9" fill="#475569">ns1-5.he.net (Hurricane Electric)</text>
<text x="1015" y="504" font-size="9" fill="#475569">DNSSEC: not enabled</text>
<text x="1015" y="517" font-size="8" fill="#94a3b8">Reverse zone: 124.174.207.in-addr.arpa</text>
<rect x="1210" y="445" width="170" height="80" rx="8" fill="#fff" stroke="#e2e8f0"/>
<text x="1295" y="462" text-anchor="middle" font-size="10" font-weight="700" fill="#374151">ARIN</text>
<text x="1220" y="478" font-size="9" fill="#475569">IP block: 207.174.124.0/23</text>
<text x="1220" y="491" font-size="9" fill="#475569">Owner: Carrier One (COTN)</text>
<text x="1220" y="504" font-size="9" fill="#475569">rDNS delegated to HestiaCP</text>
<!-- ═══ CRON JOBS ═══ -->
<rect x="20" y="545" width="1360" height="55" rx="8" fill="#f8fafc" stroke="#e2e8f0"/>
<text x="700" y="565" text-anchor="middle" font-size="11" font-weight="700" fill="#1e3a5f">Scheduled Jobs (systemd timers + cron)</text>
<text x="35" y="582" font-size="8" fill="#475569">PG backup 4x/day | MariaDB daily | MinIO mirror 3:30 AM | Forgejo dump 4 AM | Entity scraper 3 AM | Entity cache 2 AM | RMD scraper 4 AM | Renewal check 11 PM</text>
<text x="35" y="594" font-size="8" fill="#475569">Campaign warmup 8:15 AM M-F | Payment reminder every 5m | AMB scraper 6 AM | CDR retention midnight | Container security 3:30 AM | Cold wallet sweep 10 PM | Bounce watcher (daemon)</text>
<!-- Footer -->
<text x="700" y="625" text-anchor="middle" font-size="9" fill="#94a3b8">Performance West Inc. — 207.174.124.71 — Debian 13 — Docker Compose — Updated 2026-05-07</text>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB