.nornir/assets/properties-flow.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 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}
.search{fill:#1e3a5f;stroke:#38bdf8}
.edge{stroke:#64748b;stroke-width:1.5;fill:none}
.elbl{fill:#94a3b8;font-size:11px}
@media (prefers-color-scheme: light){
.box{fill:#f1f5f9;stroke:#cbd5e1}
.lbl{fill:#0f172a}.sub{fill:#475569}.elbl{fill:#475569}
.store{fill:#d1fae5;stroke:#059669}.search{fill:#dbeafe;stroke:#2563eb}
}
</style>
</defs>
<text x="20" y="26" class="lbl" font-size="15" font-weight="700">Custom properties (§8) — tag a coordinate, then find it by the property axis</text>
<!-- write surfaces -->
<rect class="box" x="20" y="60" width="200" height="86" rx="8"/>
<text x="120" y="82" class="lbl" text-anchor="middle" font-weight="700">WRITE (set/remove)</text>
<text x="120" y="101" class="sub" text-anchor="middle">gRPC SetProperty (write-gated)</text>
<text x="120" y="116" class="sub" text-anchor="middle">PUT/POST/DELETE /-/properties</text>
<text x="120" y="131" class="sub" text-anchor="middle">CLI properties set · in-process</text>
<text x="120" y="146" class="sub" text-anchor="middle">writer/admin, repo-scoped</text>
<!-- store -->
<rect class="box store" x="330" y="52" width="220" height="104" rx="8"/>
<text x="440" y="76" class="lbl" text-anchor="middle" font-weight="700">PropertyStore</text>
<text x="440" y="97" class="sub" text-anchor="middle"><data_root>/holger-props/</text>
<text x="440" y="113" class="sub" text-anchor="middle"><repo>/<coord>.props.json</text>
<text x="440" y="129" class="sub" text-anchor="middle">{ key: [values] }, last-wins</text>
<text x="440" y="146" class="sub" text-anchor="middle">path-safe (sanitize_repo/coord_key)</text>
<!-- read/get surfaces -->
<rect class="box" x="660" y="60" width="200" height="86" rx="8"/>
<text x="760" y="82" class="lbl" text-anchor="middle" font-weight="700">READ (get)</text>
<text x="760" y="101" class="sub" text-anchor="middle">gRPC GetProperties (open)</text>
<text x="760" y="116" class="sub" text-anchor="middle">GET /-/properties</text>
<text x="760" y="131" class="sub" text-anchor="middle">holger-server properties get</text>
<text x="760" y="146" class="sub" text-anchor="middle">→ { key: [values] }</text>
<!-- search axis -->
<rect class="box search" x="330" y="210" width="220" height="120" rx="8"/>
<text x="440" y="234" class="lbl" text-anchor="middle" font-weight="700">property SEARCH axis</text>
<text x="440" y="256" class="sub" text-anchor="middle">search_repos_with_properties</text>
<text x="440" y="272" class="sub" text-anchor="middle">PropertyLookup per candidate</text>
<text x="440" y="288" class="sub" text-anchor="middle">AND-composed, exact / key-presence</text>
<text x="440" y="308" class="sub" text-anchor="middle">no store ⇒ FAIL CLOSED (no hits)</text>
<!-- search surfaces -->
<rect class="box search" x="640" y="216" width="220" height="108" rx="8"/>
<text x="750" y="240" class="lbl" text-anchor="middle" font-weight="700">on all 3 read surfaces</text>
<text x="750" y="262" class="sub" text-anchor="middle">SearchRequest.properties (gRPC)</text>
<text x="750" y="278" class="sub" text-anchor="middle">GET /-/search?property=k:v</text>
<text x="750" y="298" class="sub" text-anchor="middle">search --property k=v (CLI)</text>
<line class="edge" x1="220" y1="103" x2="328" y2="104" marker-end="url(#arw)"/>
<line class="edge" x1="550" y1="104" x2="658" y2="103" marker-end="url(#arw)"/>
<text x="565" y="95" class="elbl">get</text>
<line class="edge" x1="440" y1="156" x2="440" y2="208" marker-end="url(#arw)"/>
<text x="452" y="184" class="elbl">read by axis</text>
<line class="edge" x1="550" y1="270" x2="638" y2="270" marker-end="url(#arw)"/>
</svg>