Files
ai-resume/content/assets/diagrams/homelab-topology.svg
Sam Rolfe 6ac0e1ff47 visuals: print styles for the wall and the diagrams, and Phase 7 notes
- Diagrams carry a print style block, so they print as ink on white rather than
  as dark panels; the client wall and the chips go white with a hairline.
- docs/phase-7-notes.md records what batch 1 shipped, how to add a mark or an
  image, and the Lighthouse measurements taken after the batch (100/100/100/100,
  CLS 0, mobile and desktop).

(cherry picked from commit ae9282e5f62b8c7ee149ebf96f8d3f00067a49e2)
2026-10-28 15:56:12 +11:00

110 lines
5.6 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1110" height="600" viewBox="0 0 1110 600" role="img" aria-labelledby="topoTitle topoDesc" font-family="ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif">
<title id="topoTitle">Homelab topology: build host, runtime host and public edge</title>
<desc id="topoDesc">Three hosts. The build host produces a release; the runtime host runs the site, the vector store, a workflow worker and the model gateway, all bound to loopback; the public edge terminates TLS and is the only host reachable from the internet. All three join one private mesh, and backups run in three tiers.</desc>
<style>
/* On paper the diagram is ink on white, not a dark panel. Presentation
attributes are the weakest source, so these rules win without editing
the drawing. */
@media print {
rect { fill: #fff; }
text { fill: #111; }
text[fill="#6ee7c6"] { fill: #0b5c48; }
text[fill="#8b98a8"] { fill: #444; }
line, path { stroke: #555; }
marker path { fill: #555; }
}
</style>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 10 5 0 10z" fill="#6ee7c6"/>
</marker>
</defs>
<g fill="#12161b" stroke="#33404d" stroke-width="1.5">
<rect x="30" y="104" width="310" height="268" rx="10"/>
<rect x="400" y="104" width="310" height="268" rx="10"/>
<rect x="770" y="104" width="310" height="268" rx="10"/>
</g>
<!-- internet -->
<g>
<rect x="810" y="24" width="230" height="46" rx="23" fill="#171d24" stroke="#33404d" stroke-width="1.5"/>
<text x="925" y="52" text-anchor="middle" font-size="13" fill="#b3bcc8">public internet · https :443</text>
<line x1="925" y1="70" x2="925" y2="100" stroke="#6ee7c6" stroke-width="1.5" marker-end="url(#arrow)"/>
</g>
<!-- host headers -->
<g fill="#e6edf3" font-size="15" font-weight="600">
<text x="54" y="136">Build and development</text>
<text x="424" y="136">Runtime host</text>
<text x="794" y="136">Public edge</text>
</g>
<g fill="#8b98a8" font-size="12" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">
<text x="54" y="156">.27 · 4-screen desktop</text>
<text x="424" y="156">.13 · 15 GiB · memory-bound</text>
<text x="794" y="156">.35 · Ubuntu · 2 vCPU</text>
</g>
<!-- build host items -->
<g fill="#b3bcc8" font-size="12.5">
<text x="54" y="192">· repository working copy</text>
<text x="54" y="219">· agent fleet, panes and approvals</text>
<text x="54" y="246">· content, diagrams and exhibits</text>
<text x="54" y="273">· release bundles and hashes</text>
</g>
<!-- runtime: loopback trust boundary -->
<rect x="416" y="172" width="278" height="150" rx="8" fill="none" stroke="#6ee7c6" stroke-width="1.2" stroke-dasharray="5 4"/>
<text x="430" y="192" font-size="11" fill="#6ee7c6" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">bound to 127.0.0.1</text>
<g fill="#b3bcc8" font-size="12.5">
<text x="430" y="216">· site server :8096</text>
<text x="430" y="241">· Qdrant :6333 / :6334</text>
<text x="430" y="266">· Prefect worker</text>
<text x="430" y="291">· model gateway (shared)</text>
</g>
<text x="424" y="345" font-size="11.5" fill="#8b98a8">Every cap equals its swap cap. Nothing here listens publicly.</text>
<!-- edge host items -->
<g fill="#b3bcc8" font-size="12.5">
<text x="794" y="192">· Caddy terminates TLS</text>
<text x="794" y="219">· proxies to the runtime host</text>
<text x="794" y="246">· Gitea · Immich · Nextcloud</text>
<text x="794" y="273">· one public listener, many sites</text>
</g>
<!-- hand-off arrows -->
<line x1="340" y1="230" x2="396" y2="230" stroke="#6ee7c6" stroke-width="1.5" marker-end="url(#arrow)"/>
<text x="368" y="218" text-anchor="middle" font-size="10.5" fill="#8b98a8">deploy</text>
<line x1="710" y1="230" x2="766" y2="230" stroke="#6ee7c6" stroke-width="1.5" marker-end="url(#arrow)"/>
<text x="738" y="218" text-anchor="middle" font-size="10.5" fill="#8b98a8">proxy</text>
<!-- mesh band -->
<rect x="30" y="410" width="1050" height="56" rx="10" fill="#12161b" stroke="#33404d" stroke-width="1.2" stroke-dasharray="6 5"/>
<text x="555" y="436" text-anchor="middle" font-size="13" fill="#b3bcc8">private mesh · SSH on every host · remote access without an exposed port</text>
<text x="555" y="454" text-anchor="middle" font-size="11" fill="#8b98a8">no service is reachable because it happened to bind to every interface</text>
<g stroke="#33404d" stroke-width="1" stroke-dasharray="3 4">
<line x1="185" y1="372" x2="185" y2="410"/>
<line x1="555" y1="372" x2="555" y2="410"/>
<line x1="925" y1="372" x2="925" y2="410"/>
</g>
<!-- backup tiers -->
<text x="30" y="506" font-size="13" font-weight="600" fill="#e6edf3">Three backup tiers — each covers a different failure</text>
<g fill="#12161b" stroke="#33404d" stroke-width="1.2">
<rect x="30" y="522" width="330" height="52" rx="8"/>
<rect x="390" y="522" width="330" height="52" rx="8"/>
<rect x="750" y="522" width="330" height="52" rx="8"/>
</g>
<g fill="#b3bcc8" font-size="12.5">
<text x="50" y="545">local snapshots, on the machine</text>
<text x="410" y="545">a copy on another machine</text>
<text x="770" y="545">encrypted object storage, offsite</text>
</g>
<g fill="#8b98a8" font-size="11">
<text x="50" y="562">restore tested, runbook beside it</text>
<text x="410" y="562">over the private mesh</text>
<text x="770" y="562">retention stated in the job</text>
</g>
</svg>