Files
ai-resume/content/assets/diagrams/01-homelab-topology.svg
Documenter da18ab2237 Phase 7: authored assets (diagrams, code exhibits, client wall, budget)
Phase 7 is mostly authored assets. This commit is the asset half; the
template half is a Builder child issue because this phase must not write
application code.

DoD items satisfied or advanced:

- [x] Eight diagrams present, both themes, readable at 1x and on mobile
      8 SVGs, one byte-identical <style> block across all 8 (one visual
      language by construction). Light and dark resolve from a single file
      via prefers-color-scheme, verified per file in Chrome: 8/8 return two
      distinct palettes. Geometry verified with getBBox(), not estimated:
      0 findings after fixing 74 (text overflow, partial overlap, empty
      text, out-of-viewBox). Mobile type at the 900px minimum is 14.1/12.2px.

- [x] Six code exhibits with a two-sentence rationale each
      212 verbatim lines across five eras, copied with cp/sed from the
      read-only archive. Every rationale is exactly two sentences about why
      the code is good; every exhibit also carries an honest_note, and where
      the code is not good the note says so (the 3,400-line procedural PHP
      script is presented for its operational judgement, not its structure).

- [~] Every image optimised; total page weight budgeted and recorded
      BUDGET.md records measured raw and gzip weights for all 15 served
      files: 63,802 B raw / 13,220 B gzipped. Zero rasters by design. Page
      weight of a real page is not measurable until Phase 2 templates and
      Phase 3 serving exist, and is recorded as open rather than asserted.

- [x] Archive never linked or exposed
      No mount paths or volume names under content/. Provenance recorded as
      "private archive, path withheld".

- [ ] Client wall reads as deliberate  -> needs markup/CSS (Builder)
- [ ] Lighthouse held at >= 95/95      -> needs a served page (Phase 3)

Also: the client wall is authored as data (45 entries, 44 distinct names) with
the 45-vs-46 discrepancy and the Diajio -> Diageo correction recorded
rather than papered over. No commercial marks are enabled: that is a
permission decision for Sam, so availability is recorded and permission is not
implied.
2026-10-26 17:35:15 +11:00

112 lines
6.9 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 540" width="960" height="540" role="img" aria-labelledby="ttl dsc">
<title id="ttl">Homelab topology</title>
<desc id="dsc">Three hosts with their roles and the trust boundaries between them: public internet reaches Caddy on .35, which forwards over the LAN to services bound to 127.0.0.1 on .13. .27 is build and dev with no public listeners.</desc>
<style>
text{font-family:ui-sans-serif,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;fill:#1b222b;font-size:15px}
.hd{font-size:15px;font-weight:600}
.sub{font-size:13px;fill:#5b6673}
.cap{font-size:12px;fill:#5b6673;letter-spacing:.06em;text-transform:uppercase;font-weight:600}
.big{font-size:19px;font-weight:600}
.node{fill:#f4f6f8;stroke:#c3ccd6;stroke-width:1.4}
.node-a{fill:#e9f1fd;stroke:#1a6bc4;stroke-width:1.4}
.node-t{fill:#e6f4f2;stroke:#0f766e;stroke-width:1.4}
.node-w{fill:#fdf1e3;stroke:#b45309;stroke-width:1.4}
.zone{fill:none;stroke:#aab4c0;stroke-width:1.4;stroke-dasharray:7 6}
.rule{stroke:#d0d7df;stroke-width:1}
.ln{stroke:#6b7684;stroke-width:1.6;fill:none}
.ln.d{stroke-dasharray:6 5}
.ln.a{stroke:#1a6bc4}
.ln.t{stroke:#0f766e}
.ln.w{stroke:#b45309}
.mN{fill:#6b7684}
.mA{fill:#1a6bc4}
.mT{fill:#0f766e}
.mW{fill:#b45309}
.dot{fill:#6b7684}
.tick{stroke:#aab4c0;stroke-width:1.2}
@media (prefers-color-scheme: dark){
text{fill:#e7edf4}
.sub,.cap{fill:#98a4b3}
.node{fill:#182028;stroke:#35424f}
.node-a{fill:#11253d;stroke:#5aa2f0}
.node-t{fill:#0d2624;stroke:#38b3a6}
.node-w{fill:#2a1d10;stroke:#d98b3f}
.zone{stroke:#43525f}
.rule{stroke:#2c3743}
.ln{stroke:#8496a6}
.ln.a{stroke:#5aa2f0}
.ln.t{stroke:#38b3a6}
.ln.w{stroke:#d98b3f}
.mN{fill:#8496a6}
.mA{fill:#5aa2f0}
.mT{fill:#38b3a6}
.mW{fill:#d98b3f}
.dot{fill:#8496a6}
.tick{stroke:#43525f}
}
</style>
<defs>
<marker id="aN" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="mN" d="M0,0 L10,5 L0,10 z"/></marker>
<marker id="aA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="mA" d="M0,0 L10,5 L0,10 z"/></marker>
<marker id="aT" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="mT" d="M0,0 L10,5 L0,10 z"/></marker>
<marker id="aW" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="mW" d="M0,0 L10,5 L0,10 z"/></marker>
</defs>
<text x="24" y="40" class="big">Homelab topology — hosts, roles, trust boundaries</text>
<rect x="24" y="74" width="150" height="152" rx="10" class="zone"/>
<text x="36" y="94" class="cap">public internet</text>
<rect x="38" y="110" width="122" height="62" rx="7" class="node "/>
<text x="99.0" y="139.0" class="hd" text-anchor="middle">Visitors</text>
<text x="99.0" y="157.0" class="sub" text-anchor="middle">no host access</text>
<path d="M174,148 L200,148" class="ln a" marker-end="url(#aA)"/>
<rect x="206" y="74" width="228" height="300" rx="7" class="node "/>
<text x="220" y="100" class="hd">.35 sam-Ubuntu1</text>
<text x="220" y="120" class="sub">public edge · 2 CPUs · 14 GiB</text>
<path d="M206,132 L434,132" class="rule"/>
<rect x="220" y="146" width="200" height="48" rx="7" class="node node-a"/>
<text x="320.0" y="168.0" class="hd" text-anchor="middle">Caddy :443</text>
<text x="320.0" y="186.0" class="sub" text-anchor="middle">the only public listener</text>
<rect x="220" y="208" width="200" height="44" rx="7" class="node "/>
<text x="320.0" y="235.0" class="hd" text-anchor="middle">Gitea · Immich · Wiki.js</text>
<rect x="220" y="262" width="200" height="44" rx="7" class="node "/>
<text x="320.0" y="289.0" class="hd" text-anchor="middle">Nextcloud · Penpot</text>
<text x="220" y="330" class="sub">terminates 443 for</text>
<text x="220" y="348" class="sub">*.lab.audasmedia.com.au</text>
<path d="M434,170 L462,170" class="ln a" marker-end="url(#aA)"/>
<rect x="468" y="74" width="268" height="366" rx="7" class="node "/>
<text x="482" y="100" class="hd">.13 nixos-desktop</text>
<text x="482" y="120" class="sub">runtime · 12 CPUs · 2.3 GiB free</text>
<path d="M468,132 L736,132" class="rule"/>
<rect x="482" y="146" width="240" height="46" rx="7" class="node node-a"/>
<text x="602.0" y="167.0" class="hd" text-anchor="middle">Go app :8096</text>
<text x="602.0" y="185.0" class="sub" text-anchor="middle">templ · HTMX · SSE · SQLite</text>
<rect x="482" y="200" width="240" height="42" rx="7" class="node "/>
<text x="602.0" y="226.0" class="hd" text-anchor="middle">Qdrant :6333 / :6334</text>
<rect x="482" y="250" width="240" height="42" rx="7" class="node "/>
<text x="602.0" y="269.0" class="hd" text-anchor="middle">Prefect worker</text>
<text x="602.0" y="287.0" class="sub" text-anchor="middle">content/ → chunk → embed</text>
<rect x="482" y="300" width="240" height="42" rx="7" class="node node-t"/>
<text x="602.0" y="319.0" class="hd" text-anchor="middle">OmniRoute</text>
<text x="602.0" y="337.0" class="sub" text-anchor="middle">existing · the only model gateway</text>
<rect x="482" y="350" width="240" height="42" rx="7" class="node "/>
<text x="602.0" y="369.0" class="hd" text-anchor="middle">Whisper · Piper</text>
<text x="602.0" y="387.0" class="sub" text-anchor="middle">speech in, speech out</text>
<text x="482" y="414" class="sub">every listener on 127.0.0.1</text>
<path d="M736,200 L764,200" class="ln d " marker-end="url(#aN)"/>
<text x="750.0" y="193.0" class="sub" text-anchor="middle">LAN</text>
<rect x="766" y="74" width="170" height="220" rx="7" class="node "/>
<text x="780" y="100" class="hd">.27</text>
<text x="780" y="120" class="sub">sam-4screen-desktop</text>
<path d="M766,132 L936,132" class="rule"/>
<rect x="780" y="146" width="142" height="44" rx="7" class="node "/>
<text x="851.0" y="173.0" class="hd" text-anchor="middle">content/ + source</text>
<rect x="780" y="200" width="142" height="44" rx="7" class="node "/>
<text x="851.0" y="227.0" class="hd" text-anchor="middle">agent fleet</text>
<text x="780" y="268" class="sub">build / dev · no public</text>
<text x="780" y="286" class="sub">listeners</text>
<rect x="206" y="396" width="730" height="62" rx="10" class="zone"/>
<text x="218" y="416" class="cap">trust boundary — outside this boundary nothing is reachable from the internet</text>
<text x="220" y="438" class="sub">Public → Caddy :443 on .35 → LAN → 127.0.0.1 services on .13. New footprint ≤ 1.0 GiB · ≤ 4 containers.</text>
<text x="24" y="486" class="sub">Solid = network path. Dashed = trust boundary. Ports are those the app actually binds.</text>
<text x="936" y="486" class="sub" text-anchor="end">measured 2026-10-17 · reference/stack.md</text>
</svg>