.nornir/assets/raw-repo-flow.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 860 400" font-family="ui-sans-serif,system-ui,sans-serif" font-size="13">
<defs>
<marker id="arw" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L7,3 L0,6 Z" fill="#64748b"/>
</marker>
<style>
.box{fill:#0f172a;stroke:#334155;stroke-width:1.5}
.lbl{fill:#e2e8f0}
.sub{fill:#94a3b8;font-size:11px}
.store{fill:#065f46;stroke:#10b981}
.guard{fill:#7c2d12;stroke:#f97316}
.edge{stroke:#64748b;stroke-width:1.5;fill:none}
@media (prefers-color-scheme: light){
.box{fill:#f1f5f9;stroke:#cbd5e1}
.lbl{fill:#0f172a}.sub{fill:#475569}
.store{fill:#d1fae5;stroke:#059669}.guard{fill:#ffedd5;stroke:#ea580c}
}
</style>
</defs>
<text x="20" y="26" class="lbl" font-size="15" font-weight="700">Generic / raw repo — path-addressed blob bucket, one coordinate↔path bijection</text>
<!-- HTTP entry -->
<rect class="box" x="20" y="60" width="185" height="96" rx="8"/>
<text x="112" y="84" class="lbl" text-anchor="middle" font-weight="700">HTTP door</text>
<text x="112" y="104" class="sub" text-anchor="middle">PUT/POST /raw/<path> → 201</text>
<text x="112" y="120" class="sub" text-anchor="middle">GET/HEAD /raw/<path> → 200/404</text>
<text x="112" y="136" class="sub" text-anchor="middle">DELETE /raw/<path> → 204/404</text>
<text x="112" y="151" class="sub" text-anchor="middle">checksum-deploy gate at door</text>
<!-- gRPC / in-process entry -->
<rect class="box" x="20" y="184" width="185" height="80" rx="8"/>
<text x="112" y="208" class="lbl" text-anchor="middle" font-weight="700">gRPC / in-process</text>
<text x="112" y="228" class="sub" text-anchor="middle">put/fetch(ArtifactId)</text>
<text x="112" y="244" class="sub" text-anchor="middle">id → join([ns,name,version])</text>
<text x="112" y="260" class="sub" text-anchor="middle">with '/'</text>
<!-- traversal guard -->
<rect class="box guard" x="300" y="118" width="180" height="88" rx="8"/>
<text x="390" y="142" class="lbl" text-anchor="middle" font-weight="700">safe_relpath()</text>
<text x="390" y="163" class="sub" text-anchor="middle">per-segment guard</text>
<text x="390" y="179" class="sub" text-anchor="middle">reject "" · . · .. · \ · NUL</text>
<text x="390" y="195" class="sub" text-anchor="middle">→ traversal-closed</text>
<!-- store -->
<rect class="box store" x="580" y="70" width="260" height="90" rx="8"/>
<text x="710" y="94" class="lbl" text-anchor="middle" font-weight="700">base_dir/<relpath></text>
<text x="710" y="114" class="sub" text-anchor="middle">loose-file store, atomic write-rename</text>
<text x="710" y="130" class="sub" text-anchor="middle">no archive · no index synthesis</text>
<text x="710" y="146" class="sub" text-anchor="middle">delete_artifact = sha256-verified unlink</text>
<!-- reused seams -->
<rect class="box" x="580" y="196" width="260" height="120" rx="8"/>
<text x="710" y="220" class="lbl" text-anchor="middle" font-weight="700">reused seams (RepositoryBackendTrait)</text>
<text x="710" y="242" class="sub" text-anchor="middle">list() → ArtifactId{name:<relpath>}</text>
<text x="710" y="258" class="sub" text-anchor="middle">round-trips: fetch(listed id) resolves</text>
<text x="710" y="278" class="sub" text-anchor="middle">→ search · retention/GC · replication</text>
<text x="710" y="298" class="sub" text-anchor="middle">→ metrics (Put/Get/Delete at HTTP exit)</text>
<line class="edge" x1="205" y1="108" x2="298" y2="150" marker-end="url(#arw)"/>
<line class="edge" x1="205" y1="224" x2="298" y2="176" marker-end="url(#arw)"/>
<line class="edge" x1="480" y1="150" x2="578" y2="115" marker-end="url(#arw)"/>
<line class="edge" x1="710" y1="160" x2="710" y2="194" marker-end="url(#arw)"/>
</svg>