docs/v2/img/setup-oneway.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1300 600" width="1300" height="600" role="img" aria-label="Setup B — one-way (cross-domain)" font-family="'DejaVu Sans','Segoe UI',system-ui,sans-serif">
<style>
.rule{stroke:#143a52;stroke-width:1}
.dot{fill:#16415c}
.title{fill:#eaf6ff;font-size:21px;font-weight:700}
.sub{fill:#9fc9e0;font-size:12.5px}
.bandlabel{fill:#7fa8c4;font-size:11px;letter-spacing:.06em}
.zone{fill:none;stroke:#1d4560;stroke-width:1;stroke-dasharray:3 5}
.band{fill:#0d2235;stroke:#1d4560;stroke-width:1}
.card{stroke-width:1.6}
.card.repo{fill:url(#chip);stroke:#5aa6cf}
.card.storage{fill:url(#slot);stroke:#4f8fae}
.card.source{fill:url(#rail);stroke:#c9a86f;stroke-width:2}
.card.gate{fill:url(#slot);stroke:#e0b453;stroke-width:1.8;stroke-dasharray:6 3}
.card.console{fill:url(#rail);stroke:#8fb8ff}
.card.main{fill:url(#chip);stroke:#6fd3f5;stroke-width:2.6}
.card.downstream{fill:url(#rail);stroke:#8ef0b4;stroke-width:2.2}
.card.consumer{fill:url(#slot);stroke:#7fb0cc;stroke-dasharray:4 4}
.card.step{fill:url(#chip);stroke:#7fb0cc}
.card.held{fill:url(#slot);stroke:#e0b453;stroke-width:2}
.card.refused{fill:url(#slot);stroke:#f08c8c;stroke-width:2}
.card.intel{fill:url(#rail);stroke:#d9a0ff;stroke-width:2}
.card.apps{fill:url(#chip);stroke:#ffd166;stroke-width:2.2}
.name{fill:#eaf6ff;font-size:14.5px;font-weight:700}
.kindword{fill:#6f9fbb;font-size:9px;letter-spacing:.11em}
.detail{fill:#9fc9e0;font-size:10.5px;font-family:'DejaVu Sans Mono',ui-monospace,SFMono-Regular,Menlo,monospace}
.rowlabel{fill:#86b3cc;font-size:10.5px}
.factvalue{fill:#bfe6ff;font-size:10.5px;font-family:'DejaVu Sans Mono',ui-monospace,SFMono-Regular,Menlo,monospace}
.edge{fill:none;stroke:#7fb0cc;stroke-width:1.6}
.edge.promote{stroke:#6fd3f5;stroke-width:3}
.edge.feeds{stroke:#c9a86f;stroke-width:2.4}
.edge.syncs{stroke:#8ef0b4;stroke-width:3}
.edge.ingests{stroke:#4f8fae;stroke-width:1.6;stroke-dasharray:8 3}
.edge.serves{stroke:#4f8fae;stroke-width:1.6}
.edge.consoleedge{stroke:#8fb8ff;stroke-width:1.6}
.edge.oneway{stroke:#e0b453;stroke-width:3}
.edge.carried{stroke:#c9a86f;stroke-width:2;stroke-dasharray:6 4}
.edge.intel{stroke:#d9a0ff;stroke-width:2.2}
.edge.graph{stroke:#ffd166;stroke-width:2.2}
.edge.tells{stroke:#f08c8c;stroke-width:2;stroke-dasharray:2 4}
.edge.held{stroke:#e0b453;stroke-width:2;stroke-dasharray:4 4}
.edge.refuses{stroke:#f08c8c;stroke-width:2.2}
.edgelabel{fill:#cfe6f5;font-size:10px}
.head.promote{fill:#6fd3f5}
.head.feeds,.head.carried{fill:#c9a86f}
.head.syncs{fill:#8ef0b4}
.head.ingests,.head.serves{fill:#4f8fae}
.head.consoleedge{fill:#8fb8ff}
.head.oneway,.head.held{fill:#e0b453}
.head.intel{fill:#d9a0ff}
.head.graph{fill:#ffd166}
.head.tells,.head.refuses{fill:#f08c8c}
.legendtext{fill:#cfe6f5;font-size:11px}
.pill{fill:#0b1c2e;stroke:#1d4560;stroke-width:1}
.lamp{stroke:#06111d;stroke-width:1.5}
.lamp.red{fill:#ff5c5c}
.lamp.yellow{fill:#ffd166}
.lamp.none{fill:#8ef0b4}
.lamp.unknown{fill:#6f9fbb}
.note{fill:#9fc9e0;font-size:11px}
</style>
<defs>
<linearGradient id="board" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#0b1c2e"/><stop offset="1" stop-color="#06111d"/></linearGradient>
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#14384f"/><stop offset="1" stop-color="#0e2a3e"/></linearGradient>
<linearGradient id="rail" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#173d57"/><stop offset="1" stop-color="#0c2438"/></linearGradient>
<linearGradient id="slot" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#123448"/><stop offset="1" stop-color="#0a2032"/></linearGradient>
<marker id="head-promote" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head promote" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-feeds" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head feeds" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-syncs" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head syncs" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-ingests" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head ingests" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-serves" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head serves" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-consoleedge" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head consoleedge" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-oneway" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head oneway" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-carried" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head carried" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-intel" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head intel" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-graph" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head graph" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-tells" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head tells" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-held" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head held" d="M0 0 L8 4 L0 8 z"/></marker>
<marker id="head-refuses" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path class="head refuses" d="M0 0 L8 4 L0 8 z"/></marker>
</defs>
<rect x="0" y="0" width="1300" height="600" fill="url(#board)"/>
<line class="rule" x1="0" y1="92" x2="1300" y2="92"/>
<circle class="dot" cx="12" cy="12" r="2"/>
<circle class="dot" cx="1288" cy="12" r="2"/>
<circle class="dot" cx="12" cy="588" r="2"/>
<circle class="dot" cx="1288" cy="588" r="2"/>
<text class="title" x="28" y="36">Setup B — one-way (cross-domain)</text>
<text class="sub" x="28" y="56">the server pushes signed bundles into a drop folder; a diode carries them one way to the inbox; the one-way downstream never dials</text>
<path class="edge feeds" d="M28.0 76 L58.0 76" marker-end="url(#head-feeds)"/>
<text class="legendtext" x="66.0" y="80">pulls from a source</text>
<path class="edge promote" d="M196.1 76 L226.1 76" marker-end="url(#head-promote)"/>
<text class="legendtext" x="234.1" y="80">promotes (intake step)</text>
<path class="edge ingests" d="M381.9 76 L411.9 76" marker-end="url(#head-ingests)"/>
<text class="legendtext" x="419.9" y="80">export / import</text>
<path class="edge oneway" d="M526.4 76 L556.4 76" marker-end="url(#head-oneway)"/>
<text class="legendtext" x="564.4" y="80">one direction only</text>
<path class="edge carried" d="M688.6 76 L718.6 76" marker-end="url(#head-carried)"/>
<text class="legendtext" x="726.6" y="80">carried by a person</text>
<path class="edge serves" d="M856.7 76 L886.7 76" marker-end="url(#head-serves)"/>
<text class="legendtext" x="894.7" y="80">serves</text>
<rect class="zone" x="28.0" y="112.0" width="500.0" height="460.0" rx="14"/>
<text class="bandlabel" x="42.0" y="132.0">INTERNET · holger (server) + holger-ui</text>
<rect class="zone" x="880.0" y="112.0" width="392.0" height="460.0" rx="14"/>
<text class="bandlabel" x="894.0" y="132.0">SECURE SIDE · holger-downstream, boundary: OneWay</text>
<path class="edge feeds" data-edge="sources->incoming" d="M238.0 181.0 C278.0 181.0 278.0 168.0 318.0 168.0" marker-end="url(#head-feeds)"/>
<path class="edge promote" data-edge="incoming->quarantine" d="M413.0 194.0 C413.0 224.0 413.0 192.0 413.0 222.0" marker-end="url(#head-promote)"/>
<path class="edge promote" data-edge="quarantine->approved" d="M413.0 274.0 C413.0 304.0 413.0 272.0 413.0 302.0" marker-end="url(#head-promote)"/>
<path class="edge promote" data-edge="approved->production" d="M413.0 354.0 C413.0 384.0 413.0 352.0 413.0 382.0" marker-end="url(#head-promote)"/>
<path class="edge consoleedge" data-edge="ui->approved" d="M238.0 391.0 C278.0 391.0 278.0 328.0 318.0 328.0" marker-end="url(#head-consoleedge)"/>
<path class="edge ingests" data-edge="production->drop" d="M508.0 408.0 C508.0 364.0 605.0 364.0 605.0 320.0" marker-end="url(#head-ingests)"/>
<path class="edge oneway" data-edge="drop->diode" d="M710.0 291.0 C740.0 291.0 710.0 291.0 740.0 291.0" marker-end="url(#head-oneway)"/>
<path class="edge oneway" data-edge="diode->inbox" d="M860.0 291.0 C890.0 291.0 880.0 291.0 910.0 291.0" marker-end="url(#head-oneway)"/>
<path class="edge carried" data-edge="drop->usb" d="M665.0 320.0 C665.0 401.0 694.0 401.0 694.0 482.0" marker-end="url(#head-carried)"/>
<path class="edge carried" data-edge="usb->inbox" d="M820.0 511.0 L860.0 511.0 L860.0 341.0 L935.0 341.0 L935.0 323.0" marker-end="url(#head-carried)"/>
<path class="edge ingests" data-edge="inbox->down" d="M1108.0 320.0 C1108.0 350.0 1108.0 332.0 1108.0 362.0" marker-end="url(#head-ingests)"/>
<path class="edge serves" data-edge="down->builds" d="M1108.0 458.0 C1108.0 488.0 1108.0 467.0 1108.0 497.0" marker-end="url(#head-serves)"/>
<g data-node="sources" data-kind="source">
<rect class="card source" x="48.0" y="152.0" width="190.0" height="58.0" rx="10"/>
<text class="kindword" x="61.0" y="169.0">SOURCES</text>
<text class="name" x="61.0" y="187.0">PyPI · npm · Maven</text>
<text class="detail" x="61.0" y="201.0">crates.io · OCI · Go · APK</text>
</g>
<g data-node="ui" data-kind="console">
<rect class="card console" x="48.0" y="362.0" width="190.0" height="58.0" rx="10"/>
<text class="kindword" x="61.0" y="379.0">CONSOLE</text>
<text class="name" x="61.0" y="397.0">holger-ui</text>
<text class="detail" x="61.0" y="411.0">approvals · board · audit</text>
</g>
<g data-node="incoming" data-kind="repo">
<rect class="card repo" x="318.0" y="142.0" width="190.0" height="52.0" rx="10"/>
<text class="kindword" x="331.0" y="159.0">REPOSITORY · INGESTED</text>
<text class="name" x="331.0" y="177.0">incoming</text>
</g>
<g data-node="quarantine" data-kind="repo">
<rect class="card repo" x="318.0" y="222.0" width="190.0" height="52.0" rx="10"/>
<text class="kindword" x="331.0" y="239.0">REPOSITORY · SCANNED</text>
<text class="name" x="331.0" y="257.0">quarantine</text>
</g>
<g data-node="approved" data-kind="repo">
<rect class="card repo" x="318.0" y="302.0" width="190.0" height="52.0" rx="10"/>
<text class="kindword" x="331.0" y="319.0">REPOSITORY · APPROVED</text>
<text class="name" x="331.0" y="337.0">approved</text>
</g>
<g data-node="production" data-kind="main">
<rect class="card main" x="318.0" y="382.0" width="190.0" height="52.0" rx="10"/>
<text class="kindword" x="331.0" y="399.0">REPOSITORY · AVAILABLE</text>
<text class="name" x="331.0" y="417.0">production</text>
</g>
<g data-node="drop" data-kind="storage">
<rect class="card storage" x="560.0" y="262.0" width="150.0" height="58.0" rx="10"/>
<text class="kindword" x="573.0" y="279.0">FOLDER</text>
<text class="name" x="573.0" y="297.0">drop folder</text>
<text class="detail" x="573.0" y="311.0">export.push_to</text>
</g>
<g data-node="diode" data-kind="gate">
<rect class="card gate" x="740.0" y="262.0" width="120.0" height="58.0" rx="10"/>
<text class="kindword" x="753.0" y="279.0">DIODE</text>
<text class="name" x="753.0" y="297.0">one way</text>
<text class="detail" x="753.0" y="311.0">no way back</text>
</g>
<g data-node="inbox" data-kind="storage">
<rect class="card storage" x="910.0" y="262.0" width="330.0" height="58.0" rx="10"/>
<text class="kindword" x="923.0" y="279.0">FOLDER</text>
<text class="name" x="923.0" y="297.0">inbox folder</text>
<text class="detail" x="923.0" y="311.0"><data>/bundle.inbox</text>
</g>
<g data-node="down" data-kind="downstream">
<rect class="card downstream" x="910.0" y="362.0" width="330.0" height="96.0" rx="10"/>
<text class="kindword" x="923.0" y="379.0">DOWNSTREAM</text>
<text class="name" x="923.0" y="397.0">holger-downstream</text>
<text class="detail" x="923.0" y="411.0">--features downstream (one-way)</text>
<text class="rowlabel" x="923.0" y="430.0">dials</text>
<text class="factvalue" x="1227.0" y="430.0" text-anchor="end">nothing</text>
<text class="rowlabel" x="923.0" y="446.0">checks</text>
<text class="factvalue" x="1227.0" y="446.0" text-anchor="end">signatures · hashes · DSSE</text>
</g>
<g data-node="builds" data-kind="consumer">
<rect class="card consumer" x="910.0" y="497.0" width="330.0" height="58.0" rx="10"/>
<text class="kindword" x="923.0" y="514.0">BUILD SYSTEMS</text>
<text class="name" x="923.0" y="532.0">mvn · cargo · pip/uv</text>
<text class="detail" x="923.0" y="546.0">docker/skopeo · go</text>
</g>
<g data-node="usb" data-kind="storage">
<rect class="card storage" x="640.0" y="482.0" width="180.0" height="58.0" rx="10"/>
<text class="kindword" x="653.0" y="499.0">USB STICK</text>
<text class="name" x="653.0" y="517.0">a person carries it</text>
<text class="detail" x="653.0" y="531.0">the same bundle</text>
</g>
<rect class="pill" x="260.8" y="153.0" width="34.4" height="15" rx="4"/>
<text class="edgelabel" x="278.0" y="164.0" text-anchor="middle">pull</text>
<rect class="pill" x="252.4" y="363.0" width="51.2" height="15" rx="4"/>
<text class="edgelabel" x="278.0" y="374.0" text-anchor="middle">approve</text>
<rect class="pill" x="495.2" y="369.0" width="129.6" height="15" rx="4"/>
<text class="edgelabel" x="560.0" y="380.0" text-anchor="middle">export.push, every 5m</text>
<rect class="pill" x="842.6" y="241.0" width="84.8" height="15" rx="4"/>
<text class="edgelabel" x="885.0" y="252.0" text-anchor="middle">one direction</text>
<rect class="pill" x="624.4" y="401.0" width="51.2" height="15" rx="4"/>
<text class="edgelabel" x="650.0" y="412.0" text-anchor="middle">or: USB</text>
<rect class="pill" x="1085.2" y="324.0" width="45.6" height="15" rx="4"/>
<text class="edgelabel" x="1108.0" y="335.0" text-anchor="middle">import</text>
</svg>