chartlet 0.23.0

Compile structured data into deterministic, accessible static charts
Documentation
1
<div class="chartlet-wrapper chartlet-responsive chartlet-bp-640"><style>.chartlet-wrapper.chartlet-responsive{display:block;width:100%;container-type:inline-size}.chartlet-variant-mobile{display:none}@container (max-width:639px){.chartlet-bp-640 .chartlet-variant-desktop{display:none}.chartlet-bp-640 .chartlet-variant-mobile{display:block}}</style><style>.chartlet-stage{position:relative;display:inline-block;max-width:100%;vertical-align:top}.chartlet-stage>svg{display:block}.chartlet-hotspot{position:absolute;border-radius:8px;cursor:pointer}.chartlet-focus{margin:0 0 12px 0;font-size:13px}.chartlet-focus summary{cursor:pointer;min-height:24px}.chartlet-focus fieldset{border:none;padding:0;margin:4px 0 0 0}.chartlet-focus label{display:inline-flex;align-items:center;min-height:44px;margin-right:14px;cursor:pointer;white-space:nowrap}.chartlet-focus input{margin-right:4px}.chartlet-focus input:focus-visible{outline:2px solid #2563eb;outline-offset:2px}.chartlet-wrapper:has(.chartlet-focus-node:checked) #chartlet-6880a2bcad91cc75 .chartlet-f{opacity:.18}#chartlet-6880a2bcad91cc75-hs-0{left:39%;top:1.333%;width:11.333%;height:4.667%}#chartlet-6880a2bcad91cc75-hs-1{left:40.333%;top:11.667%;width:8.667%;height:4%}#chartlet-6880a2bcad91cc75-hs-2{left:39.667%;top:25%;width:10%;height:6%}#chartlet-6880a2bcad91cc75-hs-3{left:40.333%;top:38.667%;width:8.667%;height:4.667%}#chartlet-6880a2bcad91cc75-hs-4{left:29.333%;top:51%;width:8.667%;height:6%}#chartlet-6880a2bcad91cc75-hs-5{left:40.333%;top:51.667%;width:8.667%;height:4.667%}#chartlet-6880a2bcad91cc75-hs-6{left:37.667%;top:74%;width:14%;height:5.333%}#chartlet-6880a2bcad91cc75-hs-7{left:54%;top:74%;width:8.667%;height:5.333%}#chartlet-6880a2bcad91cc75-hs-8{left:36%;top:88.667%;width:10%;height:4.667%}#chartlet-6880a2bcad91cc75-hs-9{left:51.333%;top:11.333%;width:10.667%;height:4.667%}#chartlet-6880a2bcad91cc75-hs-10{left:64%;top:89.333%;width:8.667%;height:3.333%}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-0{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-0 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-0 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-1{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-1 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-1 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-2{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-2 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-2 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-3{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-3 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-3 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-4{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-4 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-4 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-5{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-5 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-5 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-6{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-6 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-6 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-7{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-7 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-7 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-8{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-8 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-8 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-9{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-9 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-9 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75 .chartlet-f-10{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-10 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75 .chartlet-n-10 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(.chartlet-focus-node:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f{opacity:.18}#chartlet-6880a2bcad91cc75-m-hs-0{left:34.188%;top:1.235%;width:18.803%;height:5.556%}#chartlet-6880a2bcad91cc75-m-hs-1{left:34.188%;top:12.654%;width:18.803%;height:3.704%}#chartlet-6880a2bcad91cc75-m-hs-2{left:30.769%;top:25.617%;width:25.641%;height:5.556%}#chartlet-6880a2bcad91cc75-m-hs-3{left:33.333%;top:38.272%;width:20.513%;height:4.321%}#chartlet-6880a2bcad91cc75-m-hs-4{left:7.692%;top:49.691%;width:22.222%;height:5.556%}#chartlet-6880a2bcad91cc75-m-hs-5{left:33.333%;top:50.309%;width:20.513%;height:4.321%}#chartlet-6880a2bcad91cc75-m-hs-6{left:32.479%;top:70.988%;width:22.222%;height:6.173%}#chartlet-6880a2bcad91cc75-m-hs-7{left:58.12%;top:71.605%;width:18.803%;height:4.938%}#chartlet-6880a2bcad91cc75-m-hs-8{left:27.35%;top:85.802%;width:22.222%;height:4.321%}#chartlet-6880a2bcad91cc75-m-hs-9{left:56.41%;top:11.728%;width:22.222%;height:5.556%}#chartlet-6880a2bcad91cc75-m-hs-10{left:75.214%;top:86.42%;width:22.222%;height:3.086%}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-0{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-0 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-0:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-0 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-1{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-1 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-1:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-1 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-2{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-2 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-2:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-2 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-3{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-3 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-3:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-3 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-4{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-4 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-4:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-4 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-5{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-5 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-5:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-5 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-6{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-6 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-6:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-6 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-7{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-7 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-7:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-7 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-8{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-8 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-8:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-8 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-9{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-9 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-9:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-9 .chartlet-seq-box{stroke-width:3}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75-m .chartlet-f-10{opacity:1}.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-10 .chartlet-flow-node,.chartlet-wrapper:has(#chartlet-6880a2bcad91cc75-focus-10:checked) #chartlet-6880a2bcad91cc75-m .chartlet-n-10 .chartlet-seq-box{stroke-width:3}</style><details class="chartlet-focus"><summary>Focus</summary><fieldset><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" checked> Show all</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-0"> Teacher, parent</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-1"> Browser</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-2"> Reverse proxy</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-3"> Web / BFF</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-4"> Sign-in</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-5"> Domain API</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-6"> Domain data</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-7"> Audit log</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-8"> Backup</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-9"> School records</label><label><input type="radio" name="chartlet-6880a2bcad91cc75-focus" class="chartlet-focus-node" id="chartlet-6880a2bcad91cc75-focus-10"> SIEM / SOC</label></fieldset></details><figure class="chartlet-figure" data-viz="chart"><figcaption>Tenant architecture: the sign-in and data path</figcaption><div class="chartlet-variant-desktop"><div class="chartlet-stage"><svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" viewBox="0 0 1200 1200" id="chartlet-6880a2bcad91cc75" role="img" aria-labelledby="chartlet-6880a2bcad91cc75-title chartlet-6880a2bcad91cc75-description" class="chartlet-root"><title id="chartlet-6880a2bcad91cc75-title">Tenant architecture: the sign-in and data path</title><desc id="chartlet-6880a2bcad91cc75-description">Architecture diagram with 11 components and 10 connections in 3 boundaries. Data center: Reverse proxy, Application network. Application network (in Data center): Web / BFF, Sign-in, Domain API, Data zone. Data zone (in Application network): Domain data, Audit log. Main path: Teacher, parent → Browser → Reverse proxy → Web / BFF → Domain API → Domain data. Most connected: Domain API (5 connections). Nothing leads into Teacher, parent and School records. Teacher, parent (person): connects to Browser (uses). Browser (frontend): connects to Reverse proxy (calls, via HTTPS). School records (external system): connects to Domain API (master data, via REST/JSON). Reverse proxy (security): connects to Web / BFF (forwards). Web / BFF (service): connects to Sign-in (signs in, via OIDC) and Domain API (queries, via user context). Sign-in (security): no outgoing connection. Domain API (service): connects to Domain data (reads, writes, via SQL/TLS), Audit log (logs) and SIEM / SOC (events, via Syslog). Domain data (database): connects to Backup (backs up). Audit log (database): no outgoing connection. Backup (storage): no outgoing connection. SIEM / SOC (external system): no outgoing connection. Emphasized: Sign-in. Emphasized path: Web / BFF → Domain API → Domain data. Legend: person, frontend, service, database, storage, security, external system, emphasized, solid line, dashed line, emphasized path.</desc><style>.chartlet-root{--chartlet-text:#172033;--chartlet-muted:#344054;--chartlet-grid:#d9dee8;--chartlet-zero:#667085;--chartlet-accent:#2563eb;--chartlet-background:#fff;--chartlet-color-1:#2563eb;--chartlet-color-2:#c2410c;--chartlet-color-3:#475569;--chartlet-color-4:#111827;max-width:100%;height:auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:var(--chartlet-text)}#chartlet-6880a2bcad91cc75 .chartlet-title{font-size:22px;font-weight:650;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75 .chartlet-label,#chartlet-6880a2bcad91cc75 .chartlet-tick,#chartlet-6880a2bcad91cc75 .chartlet-value,#chartlet-6880a2bcad91cc75 .chartlet-value-inverse,#chartlet-6880a2bcad91cc75 .chartlet-axis-title{font-size:12px;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75 .chartlet-value,#chartlet-6880a2bcad91cc75 .chartlet-value-inverse{font-weight:600}#chartlet-6880a2bcad91cc75 .chartlet-value{fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75 .chartlet-value-inverse{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75 .chartlet-axis-title{font-weight:600}#chartlet-6880a2bcad91cc75 .chartlet-grid{stroke:var(--chartlet-grid);stroke-width:1}#chartlet-6880a2bcad91cc75 .chartlet-zero{stroke:var(--chartlet-zero);stroke-width:1.5}#chartlet-6880a2bcad91cc75 .chartlet-bar,#chartlet-6880a2bcad91cc75 .chartlet-point{fill:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75 .chartlet-line{fill:none;stroke:var(--chartlet-accent);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-background{fill:var(--chartlet-background)}.chartlet-root{--chartlet-node:#eef3fd;--chartlet-lane:#f7f9fc;--chartlet-lane-head:#eef1f6;--chartlet-shadow:rgba(16,24,40,.09);--chartlet-role-blue:#2563eb;--chartlet-role-blue-fill:#eef3fd;--chartlet-role-green:#15803d;--chartlet-role-green-fill:#ecfdf3;--chartlet-role-amber:#b45309;--chartlet-role-amber-fill:#fff7e8;--chartlet-role-teal:#0f766e;--chartlet-role-teal-fill:#e9faf7;--chartlet-role-violet:#6d28d9;--chartlet-role-violet-fill:#f3efff;--chartlet-role-gray:#667085;--chartlet-role-gray-fill:#f7f8fa;--chartlet-role-red:#b42318;--chartlet-role-red-fill:#fef3f2}.chartlet-root.chartlet-theme-dark{--chartlet-node:#1a2436;--chartlet-lane:#121925;--chartlet-lane-head:#18212f;--chartlet-shadow:rgba(0,0,0,.45);--chartlet-role-blue:#7ea6ff;--chartlet-role-blue-fill:#1a2436;--chartlet-role-green:#4ade80;--chartlet-role-green-fill:#132a1d;--chartlet-role-amber:#fbbf24;--chartlet-role-amber-fill:#2b2210;--chartlet-role-teal:#2dd4bf;--chartlet-role-teal-fill:#0f2a27;--chartlet-role-violet:#b69cff;--chartlet-role-violet-fill:#231c38;--chartlet-role-gray:#9aa7bd;--chartlet-role-gray-fill:#161d29;--chartlet-role-red:#f97066;--chartlet-role-red-fill:#2d1616}#chartlet-6880a2bcad91cc75 .chartlet-role-blue{fill:var(--chartlet-role-blue-fill);stroke:var(--chartlet-role-blue)}#chartlet-6880a2bcad91cc75 .chartlet-role-green{fill:var(--chartlet-role-green-fill);stroke:var(--chartlet-role-green)}#chartlet-6880a2bcad91cc75 .chartlet-role-amber{fill:var(--chartlet-role-amber-fill);stroke:var(--chartlet-role-amber)}#chartlet-6880a2bcad91cc75 .chartlet-role-teal{fill:var(--chartlet-role-teal-fill);stroke:var(--chartlet-role-teal)}#chartlet-6880a2bcad91cc75 .chartlet-role-violet{fill:var(--chartlet-role-violet-fill);stroke:var(--chartlet-role-violet)}#chartlet-6880a2bcad91cc75 .chartlet-role-gray{fill:var(--chartlet-role-gray-fill);stroke:var(--chartlet-role-gray)}#chartlet-6880a2bcad91cc75 .chartlet-role-red{fill:var(--chartlet-role-red-fill);stroke:var(--chartlet-role-red)}#chartlet-6880a2bcad91cc75 .chartlet-diagram-rim{fill:none;stroke:var(--chartlet-role-teal);stroke-width:1.5}#chartlet-6880a2bcad91cc75 .chartlet-diagram-shadow{fill:var(--chartlet-shadow);stroke:none;rx:7px}#chartlet-6880a2bcad91cc75 .chartlet-diagram-chip{fill:var(--chartlet-background);stroke:var(--chartlet-grid);stroke-width:1;rx:4px}#chartlet-6880a2bcad91cc75 .chartlet-diagram-chip-text{font-size:12px;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75 .chartlet-diagram-badge{fill:var(--chartlet-accent);stroke:var(--chartlet-background);stroke-width:2}#chartlet-6880a2bcad91cc75 .chartlet-diagram-badge-text{font-size:10px;font-weight:700;fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75 .chartlet-flow-node{stroke-width:1.5;stroke-linejoin:round;rx:7px}#chartlet-6880a2bcad91cc75 .chartlet-flow-start{stroke-width:2}#chartlet-6880a2bcad91cc75 .chartlet-flow-end{stroke-width:3}#chartlet-6880a2bcad91cc75 .chartlet-flow-external{stroke-dasharray:5 3}#chartlet-6880a2bcad91cc75 .chartlet-flow-inner{stroke:var(--chartlet-role-blue);stroke-width:1}#chartlet-6880a2bcad91cc75 .chartlet-flow-label{font-size:13px;font-weight:600;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75 .chartlet-flow-sublabel{font-size:11px;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75 .chartlet-flow-edge{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-flow-dashed{stroke-dasharray:5 4}#chartlet-6880a2bcad91cc75 .chartlet-flow-dotted{stroke-dasharray:1 4;stroke-linecap:round}#chartlet-6880a2bcad91cc75 .chartlet-flow-main{stroke:var(--chartlet-accent);stroke-width:2.2}#chartlet-6880a2bcad91cc75 .chartlet-flow-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero);stroke-width:1;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-flow-main-head{fill:var(--chartlet-accent);stroke:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75 .chartlet-flow-lane{fill:var(--chartlet-lane);stroke:var(--chartlet-grid);stroke-width:1}#chartlet-6880a2bcad91cc75 .chartlet-flow-lane-alt{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75 .chartlet-flow-lane-head{fill:var(--chartlet-lane-head);stroke:none}#chartlet-6880a2bcad91cc75 .chartlet-flow-lane-label{font-size:11px;font-weight:650;fill:var(--chartlet-muted);letter-spacing:.04em}#chartlet-6880a2bcad91cc75 .chartlet-flow-group{fill:var(--chartlet-accent);fill-opacity:.04;stroke:var(--chartlet-zero);stroke-width:1;stroke-dasharray:4 3;rx:10px}#chartlet-6880a2bcad91cc75 .chartlet-flow-group-label{font-size:11px;font-weight:600;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75 .chartlet-arch-head{fill:var(--chartlet-role-violet-fill);stroke:var(--chartlet-role-violet);stroke-width:1.5}#chartlet-6880a2bcad91cc75 .chartlet-arch-dot{fill:var(--chartlet-role-blue);stroke:none}#chartlet-6880a2bcad91cc75 .chartlet-emph .chartlet-flow-node,#chartlet-6880a2bcad91cc75 .chartlet-emph .chartlet-seq-box{stroke:var(--chartlet-accent);stroke-width:3.5;fill:var(--chartlet-accent);fill-opacity:.14}#chartlet-6880a2bcad91cc75 .chartlet-emph .chartlet-diagram-shadow{fill:none}#chartlet-6880a2bcad91cc75 .chartlet-emph .chartlet-seq-actor,#chartlet-6880a2bcad91cc75 .chartlet-emph .chartlet-seq-actor-head{stroke:var(--chartlet-accent);stroke-width:2.6}#chartlet-6880a2bcad91cc75 .chartlet-emph-ring{fill:none;stroke:var(--chartlet-accent);stroke-width:3}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-flow-edge,#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-seq-message{stroke:var(--chartlet-accent);stroke-width:3.2}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-flow-head,#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-seq-head{fill:var(--chartlet-accent);stroke:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-flow-hollow{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-flow-solid{fill:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-flow-open,#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-seq-head-open{stroke:var(--chartlet-accent);stroke-width:2.4}#chartlet-6880a2bcad91cc75 .chartlet-emph-edge .chartlet-diagram-chip-text{font-weight:700}#chartlet-6880a2bcad91cc75 .chartlet-emph-quiet .chartlet-flow-main{stroke:var(--chartlet-zero);stroke-width:1.4}#chartlet-6880a2bcad91cc75 .chartlet-emph-quiet .chartlet-flow-main-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero)}#chartlet-6880a2bcad91cc75 .chartlet-legend-line{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-legend-dashed{stroke-dasharray:5 4}#chartlet-6880a2bcad91cc75 .chartlet-legend-dotted{stroke-dasharray:1 4;stroke-linecap:round}#chartlet-6880a2bcad91cc75 .chartlet-legend-main{stroke:var(--chartlet-accent);stroke-width:2.2}#chartlet-6880a2bcad91cc75 .chartlet-legend-path{stroke:var(--chartlet-accent);stroke-width:3.2}#chartlet-6880a2bcad91cc75 .chartlet-legend-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero);stroke-width:1;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-legend-open{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}#chartlet-6880a2bcad91cc75 .chartlet-legend-emph{fill:var(--chartlet-accent);fill-opacity:.14;stroke:var(--chartlet-accent);stroke-width:3.5;rx:4px}#chartlet-6880a2bcad91cc75 .chartlet-legend-text{font-size:12px;fill:var(--chartlet-muted)}</style><rect x="328" y="272" width="460" height="716" class="chartlet-flow-group"/><text x="336" y="285" text-anchor="start" class="chartlet-flow-group-label">Data center</text><rect x="340" y="436" width="436" height="540" class="chartlet-flow-group"/><text x="348" y="449" text-anchor="start" class="chartlet-flow-group-label">Application network</text><rect x="440" y="860" width="324" height="104" class="chartlet-flow-group"/><text x="448" y="873" text-anchor="start" class="chartlet-flow-group-label">Data zone</text><g class="chartlet-f chartlet-f-0 chartlet-f-1 chartlet-emph-quiet"><polyline points="536,72 536,132" class="chartlet-flow-edge chartlet-flow-main"><title>Teacher, parent → Browser: uses</title></polyline><polyline points="532.5,132 536,140 539.5,132 532.5,132" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-1 chartlet-f-2 chartlet-emph-quiet"><polyline points="536,188 536,292" class="chartlet-flow-edge chartlet-flow-main"><title>Browser → Reverse proxy: calls [HTTPS]</title></polyline><polyline points="532.5,292 536,300 539.5,292 532.5,292" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-2 chartlet-f-3 chartlet-emph-quiet"><polyline points="536,372 536,456" class="chartlet-flow-edge chartlet-flow-main"><title>Reverse proxy → Web / BFF: forwards</title></polyline><polyline points="532.5,456 536,464 539.5,456 532.5,456" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-3 chartlet-f-4 chartlet-emph-quiet"><polyline points="512,520 512,584.125 404,584.125 404,604" class="chartlet-flow-edge"><title>Web / BFF → Sign-in: signs in [OIDC]</title></polyline><polyline points="400.5,604 404,612 407.5,604 400.5,604" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-3 chartlet-f-5 chartlet-emph-edge"><polyline points="536,520 536,612" class="chartlet-flow-edge chartlet-flow-main"><title>Web / BFF → Domain API: queries [user context]</title></polyline><polyline points="532.5,612 536,620 539.5,612 532.5,612" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-6 chartlet-emph-edge"><polyline points="536,676 536,880" class="chartlet-flow-edge chartlet-flow-main"><title>Domain API → Domain data: reads, writes [SQL/TLS]</title></polyline><polyline points="532.5,880 536,888 539.5,880 532.5,880" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-7 chartlet-emph-quiet"><polyline points="558,676 558,828.281 700,828.281 700,880" class="chartlet-flow-edge"><title>Domain API → Audit log: logs</title></polyline><polyline points="696.5,880 700,888 703.5,880 696.5,880" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-9 chartlet-emph-quiet"><polyline points="680,192 680,584.125 560,584.125 560,612" class="chartlet-flow-edge"><title>School records → Domain API: master data [REST/JSON]</title></polyline><polyline points="556.5,612 560,620 563.5,612 556.5,612" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-10 chartlet-emph-quiet"><polyline points="580,676 580,815.456 864,815.456 864,1038.569 820,1038.569 820,1064" class="chartlet-flow-edge chartlet-flow-dashed"><title>Domain API → SIEM / SOC: events [Syslog]</title></polyline><polyline points="816.5,1064 820,1072 823.5,1064 816.5,1064" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-6 chartlet-f-8 chartlet-emph-quiet"><polyline points="536,952 536,1038.569 492,1038.569 492,1056" class="chartlet-flow-edge chartlet-flow-dashed"><title>Domain data → Backup: backs up</title></polyline><polyline points="488.5,1056 492,1064 495.5,1056 488.5,1056" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2"></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-3"></g><g class="chartlet-f chartlet-n-3 chartlet-f-2 chartlet-f-3 chartlet-f-4 chartlet-f-5"></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4"></g><g class="chartlet-f chartlet-n-5 chartlet-f-3 chartlet-f-5 chartlet-f-6 chartlet-f-7 chartlet-f-9 chartlet-f-10"></g><g class="chartlet-f chartlet-n-6 chartlet-f-5 chartlet-f-6 chartlet-f-8"></g><g class="chartlet-f chartlet-n-7 chartlet-f-5 chartlet-f-7"></g><g class="chartlet-f chartlet-n-8 chartlet-f-6 chartlet-f-8"></g><g class="chartlet-f chartlet-n-9 chartlet-f-5 chartlet-f-9"></g><g class="chartlet-f chartlet-n-10 chartlet-f-5 chartlet-f-10"></g><g class="chartlet-f chartlet-f-0 chartlet-f-1 chartlet-emph-quiet"><rect x="540" y="77" width="36.88" height="21" class="chartlet-diagram-chip"/><text x="545" y="91" text-anchor="start" class="chartlet-diagram-chip-text">uses</text></g><g class="chartlet-f chartlet-f-1 chartlet-f-2 chartlet-emph-quiet"><rect x="540" y="193" width="63.64" height="36" class="chartlet-diagram-chip"/><text x="545" y="207" text-anchor="start" class="chartlet-diagram-chip-text">calls</text><text x="545" y="222" text-anchor="start" class="chartlet-diagram-chip-text">[HTTPS]</text></g><g class="chartlet-f chartlet-f-2 chartlet-f-3 chartlet-emph-quiet"><rect x="540" y="377" width="67.84" height="21" class="chartlet-diagram-chip"/><text x="545" y="391" text-anchor="start" class="chartlet-diagram-chip-text">forwards</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-4 chartlet-emph-quiet"><rect x="453.36" y="525" width="54.64" height="36" class="chartlet-diagram-chip"/><text x="503" y="539" text-anchor="end" class="chartlet-diagram-chip-text">signs in</text><text x="503" y="554" text-anchor="end" class="chartlet-diagram-chip-text">[OIDC]</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-5 chartlet-emph-edge"><rect x="540" y="525" width="101.2" height="36" class="chartlet-diagram-chip"/><text x="545" y="539" text-anchor="start" class="chartlet-diagram-chip-text">queries</text><text x="545" y="554" text-anchor="start" class="chartlet-diagram-chip-text">[user context]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-6 chartlet-emph-edge"><rect x="439.68" y="687" width="92.32" height="36" class="chartlet-diagram-chip"/><text x="527" y="701" text-anchor="end" class="chartlet-diagram-chip-text">reads, writes</text><text x="527" y="716" text-anchor="end" class="chartlet-diagram-chip-text">[SQL/TLS]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-7 chartlet-emph-quiet"><rect x="612.12" y="832.281" width="33.76" height="21" class="chartlet-diagram-chip"/><text x="629" y="846.281" text-anchor="middle" class="chartlet-diagram-chip-text">logs</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-9 chartlet-emph-quiet"><rect x="684" y="197" width="94.48" height="36" class="chartlet-diagram-chip"/><text x="689" y="211" text-anchor="start" class="chartlet-diagram-chip-text">master data</text><text x="689" y="226" text-anchor="start" class="chartlet-diagram-chip-text">[REST/JSON]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-10 chartlet-emph-quiet"><rect x="584" y="681" width="61.96" height="36" class="chartlet-diagram-chip"/><text x="589" y="695" text-anchor="start" class="chartlet-diagram-chip-text">events</text><text x="589" y="710" text-anchor="start" class="chartlet-diagram-chip-text">[Syslog]</text></g><g class="chartlet-f chartlet-f-6 chartlet-f-8 chartlet-emph-quiet"><rect x="540" y="991" width="60.88" height="21" class="chartlet-diagram-chip"/><text x="545" y="1005" text-anchor="start" class="chartlet-diagram-chip-text">backs up</text></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"><rect x="468" y="18.5" width="136" height="56" class="chartlet-diagram-shadow"/><rect x="468" y="16" width="136" height="56" class="chartlet-flow-node chartlet-role-violet"><title>Teacher, parent</title></rect><circle cx="536" cy="27" r="6" class="chartlet-arch-head"/><text x="536" y="56.5" text-anchor="middle" class="chartlet-flow-label">Teacher, parent</text></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2"><rect x="484" y="142.5" width="104" height="48" class="chartlet-diagram-shadow"/><rect x="484" y="140" width="104" height="48" class="chartlet-flow-node chartlet-role-blue"><title>Browser</title></rect><line x1="484" y1="152" x2="588" y2="152" class="chartlet-flow-inner"/><circle cx="492" cy="146" r="1.8" class="chartlet-arch-dot"/><circle cx="498" cy="146" r="1.8" class="chartlet-arch-dot"/><circle cx="504" cy="146" r="1.8" class="chartlet-arch-dot"/><text x="536" y="174.5" text-anchor="middle" class="chartlet-flow-label">Browser</text></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-3"><polyline points="476,302.5 596,302.5 596,345.7 536,374.5 476,345.7 476,302.5" class="chartlet-diagram-shadow"/><polyline points="476,300 596,300 596,343.2 536,372 476,343.2 476,300" class="chartlet-flow-node chartlet-role-red"><title>Reverse proxy – TLS 1.3, WAF</title></polyline><text x="536" y="327.5" text-anchor="middle" class="chartlet-flow-label">Reverse proxy</text><text x="536" y="341.5" text-anchor="middle" class="chartlet-flow-sublabel">TLS 1.3, WAF</text></g><g class="chartlet-f chartlet-n-3 chartlet-f-2 chartlet-f-3 chartlet-f-4 chartlet-f-5"><rect x="484" y="466.5" width="104" height="56" class="chartlet-diagram-shadow"/><rect x="484" y="464" width="104" height="56" class="chartlet-flow-node chartlet-role-blue"><title>Web / BFF – SvelteKit</title></rect><text x="536" y="489.5" text-anchor="middle" class="chartlet-flow-label">Web / BFF</text><text x="536" y="503.5" text-anchor="middle" class="chartlet-flow-sublabel">SvelteKit</text></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4 chartlet-emph"><polyline points="352,614.5 456,614.5 456,657.7 404,686.5 352,657.7 352,614.5" class="chartlet-diagram-shadow"/><polyline points="352,612 456,612 456,655.2 404,684 352,655.2 352,612" class="chartlet-flow-node chartlet-role-red"><title>Sign-in – Keycloak, OIDC</title></polyline><text x="404" y="639.5" text-anchor="middle" class="chartlet-flow-label">Sign-in</text><text x="404" y="653.5" text-anchor="middle" class="chartlet-flow-sublabel">Keycloak, OIDC</text></g><g class="chartlet-f chartlet-n-5 chartlet-f-3 chartlet-f-5 chartlet-f-6 chartlet-f-7 chartlet-f-9 chartlet-f-10"><rect x="484" y="622.5" width="104" height="56" class="chartlet-diagram-shadow"/><rect x="484" y="620" width="104" height="56" class="chartlet-flow-node chartlet-role-blue"><title>Domain API – Rust</title></rect><text x="536" y="645.5" text-anchor="middle" class="chartlet-flow-label">Domain API</text><text x="536" y="659.5" text-anchor="middle" class="chartlet-flow-sublabel">Rust</text></g><g class="chartlet-f chartlet-n-6 chartlet-f-5 chartlet-f-6 chartlet-f-8"><polyline points="452,895.5 452,949.5 454.862,950.794 463.254,952 476.603,953.036 494,953.83 514.259,954.33 536,954.5 557.741,954.33 578,953.83 595.397,953.036 608.746,952 617.138,950.794 620,949.5 620,895.5 617.138,894.206 608.746,893 595.397,891.964 578,891.17 557.741,890.67 536,890.5 514.259,890.67 494,891.17 476.603,891.964 463.254,893 454.862,894.206 452,895.5 452,895.5" class="chartlet-diagram-shadow"/><polyline points="452,893 452,947 454.862,948.294 463.254,949.5 476.603,950.536 494,951.33 514.259,951.83 536,952 557.741,951.83 578,951.33 595.397,950.536 608.746,949.5 617.138,948.294 620,947 620,893 617.138,891.706 608.746,890.5 595.397,889.464 578,888.67 557.741,888.17 536,888 514.259,888.17 494,888.67 476.603,889.464 463.254,890.5 454.862,891.706 452,893 452,893" class="chartlet-flow-node chartlet-role-teal"><title>Domain data – Postgres, RLS per tenant</title></polyline><polyline points="452,893 454.862,894.294 463.254,895.5 476.603,896.536 494,897.33 514.259,897.83 536,898 557.741,897.83 578,897.33 595.397,896.536 608.746,895.5 617.138,894.294 620,893" class="chartlet-diagram-rim"/><text x="536" y="920.5" text-anchor="middle" class="chartlet-flow-label">Domain data</text><text x="536" y="934.5" text-anchor="middle" class="chartlet-flow-sublabel">Postgres, RLS per tenant</text></g><g class="chartlet-f chartlet-n-7 chartlet-f-5 chartlet-f-7"><polyline points="648,895.5 648,949.5 649.772,950.794 654.967,952 663.23,953.036 674,953.83 686.541,954.33 700,954.5 713.459,954.33 726,953.83 736.77,953.036 745.033,952 750.228,950.794 752,949.5 752,895.5 750.228,894.206 745.033,893 736.77,891.964 726,891.17 713.459,890.67 700,890.5 686.541,890.67 674,891.17 663.23,891.964 654.967,893 649.772,894.206 648,895.5 648,895.5" class="chartlet-diagram-shadow"/><polyline points="648,893 648,947 649.772,948.294 654.967,949.5 663.23,950.536 674,951.33 686.541,951.83 700,952 713.459,951.83 726,951.33 736.77,950.536 745.033,949.5 750.228,948.294 752,947 752,893 750.228,891.706 745.033,890.5 736.77,889.464 726,888.67 713.459,888.17 700,888 686.541,888.17 674,888.67 663.23,889.464 654.967,890.5 649.772,891.706 648,893 648,893" class="chartlet-flow-node chartlet-role-teal"><title>Audit log – append only</title></polyline><polyline points="648,893 649.772,894.294 654.967,895.5 663.23,896.536 674,897.33 686.541,897.83 700,898 713.459,897.83 726,897.33 736.77,896.536 745.033,895.5 750.228,894.294 752,893" class="chartlet-diagram-rim"/><text x="700" y="920.5" text-anchor="middle" class="chartlet-flow-label">Audit log</text><text x="700" y="934.5" text-anchor="middle" class="chartlet-flow-sublabel">append only</text></g><g class="chartlet-f chartlet-n-8 chartlet-f-6 chartlet-f-8"><polyline points="432,1066.5 552,1066.5 544,1122.5 440,1122.5 432,1066.5" class="chartlet-diagram-shadow"/><polyline points="432,1064 552,1064 544,1120 440,1120 432,1064" class="chartlet-flow-node chartlet-role-teal"><title>Backup – second site</title></polyline><text x="492" y="1089.5" text-anchor="middle" class="chartlet-flow-label">Backup</text><text x="492" y="1103.5" text-anchor="middle" class="chartlet-flow-sublabel">second site</text></g><g class="chartlet-f chartlet-n-9 chartlet-f-5 chartlet-f-9"><rect x="616" y="138.5" width="128" height="56" class="chartlet-diagram-shadow"/><rect x="616" y="136" width="128" height="56" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"><title>School records – legacy system</title></rect><text x="680" y="161.5" text-anchor="middle" class="chartlet-flow-label">School records</text><text x="680" y="175.5" text-anchor="middle" class="chartlet-flow-sublabel">legacy system</text></g><g class="chartlet-f chartlet-n-10 chartlet-f-5 chartlet-f-10"><rect x="768" y="1074.5" width="104" height="40" class="chartlet-diagram-shadow"/><rect x="768" y="1072" width="104" height="40" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"><title>SIEM / SOC</title></rect><text x="820" y="1096.5" text-anchor="middle" class="chartlet-flow-label">SIEM / SOC</text></g><g class="chartlet-legend-strip"><rect x="24" y="1150.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="24" y="1148" width="26" height="16" class="chartlet-flow-node chartlet-role-violet"/><circle cx="37" cy="1159" r="6" class="chartlet-arch-head"/><text x="58" y="1160" text-anchor="start" class="chartlet-legend-text">Person</text><rect x="121.64" y="1150.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="121.64" y="1148" width="26" height="16" class="chartlet-flow-node chartlet-role-blue"/><line x1="121.64" y1="1160" x2="147.64" y2="1160" class="chartlet-flow-inner"/><circle cx="129.64" cy="1154" r="1.8" class="chartlet-arch-dot"/><circle cx="135.64" cy="1154" r="1.8" class="chartlet-arch-dot"/><circle cx="141.64" cy="1154" r="1.8" class="chartlet-arch-dot"/><text x="155.64" y="1160" text-anchor="start" class="chartlet-legend-text">Frontend</text><rect x="232.72" y="1150.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="232.72" y="1148" width="26" height="16" class="chartlet-flow-node chartlet-role-blue"/><text x="266.72" y="1160" text-anchor="start" class="chartlet-legend-text">Service</text><polyline points="333.96,1155.5 333.96,1161.5 334.403,1162.794 335.702,1164 337.768,1165.036 340.46,1165.83 343.595,1166.33 346.96,1166.5 350.325,1166.33 353.46,1165.83 356.152,1165.036 358.218,1164 359.517,1162.794 359.96,1161.5 359.96,1155.5 359.517,1154.206 358.218,1153 356.152,1151.964 353.46,1151.17 350.325,1150.67 346.96,1150.5 343.595,1150.67 340.46,1151.17 337.768,1151.964 335.702,1153 334.403,1154.206 333.96,1155.5 333.96,1155.5" class="chartlet-diagram-shadow"/><polyline points="333.96,1153 333.96,1159 334.403,1160.294 335.702,1161.5 337.768,1162.536 340.46,1163.33 343.595,1163.83 346.96,1164 350.325,1163.83 353.46,1163.33 356.152,1162.536 358.218,1161.5 359.517,1160.294 359.96,1159 359.96,1153 359.517,1151.706 358.218,1150.5 356.152,1149.464 353.46,1148.67 350.325,1148.17 346.96,1148 343.595,1148.17 340.46,1148.67 337.768,1149.464 335.702,1150.5 334.403,1151.706 333.96,1153 333.96,1153" class="chartlet-flow-node chartlet-role-teal"/><polyline points="333.96,1153 334.403,1154.294 335.702,1155.5 337.768,1156.536 340.46,1157.33 343.595,1157.83 346.96,1158 350.325,1157.83 353.46,1157.33 356.152,1156.536 358.218,1155.5 359.517,1154.294 359.96,1153" class="chartlet-diagram-rim"/><text x="367.96" y="1160" text-anchor="start" class="chartlet-legend-text">Database</text><polyline points="445.04,1150.5 471.04,1150.5 463.04,1166.5 453.04,1166.5 445.04,1150.5" class="chartlet-diagram-shadow"/><polyline points="445.04,1148 471.04,1148 463.04,1164 453.04,1164 445.04,1148" class="chartlet-flow-node chartlet-role-teal"/><text x="479.04" y="1160" text-anchor="start" class="chartlet-legend-text">Storage</text><polyline points="549.4,1150.5 575.4,1150.5 575.4,1160.1 562.4,1166.5 549.4,1160.1 549.4,1150.5" class="chartlet-diagram-shadow"/><polyline points="549.4,1148 575.4,1148 575.4,1157.6 562.4,1164 549.4,1157.6 549.4,1148" class="chartlet-flow-node chartlet-role-red"/><text x="583.4" y="1160" text-anchor="start" class="chartlet-legend-text">Security</text><rect x="657.36" y="1150.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="657.36" y="1148" width="26" height="16" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"/><text x="691.36" y="1160" text-anchor="start" class="chartlet-legend-text">External system</text><rect x="813.56" y="1148" width="26" height="16" class="chartlet-legend-emph"/><text x="847.56" y="1160" text-anchor="start" class="chartlet-legend-text">Emphasized</text><line x1="939.04" y1="1156" x2="965.04" y2="1156" class="chartlet-legend-line"/><text x="973.04" y="1160" text-anchor="start" class="chartlet-legend-text">Solid line</text><line x1="1048.2" y1="1156" x2="1074.2" y2="1156" class="chartlet-legend-line chartlet-legend-dashed"/><text x="1082.2" y="1160" text-anchor="start" class="chartlet-legend-text">Dashed line</text><line x1="24" y1="1180" x2="50" y2="1180" class="chartlet-legend-line chartlet-legend-path"/><text x="58" y="1184" text-anchor="start" class="chartlet-legend-text">Emphasized path</text></g></svg><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-0" for="chartlet-6880a2bcad91cc75-focus-0"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-1" for="chartlet-6880a2bcad91cc75-focus-1"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-2" for="chartlet-6880a2bcad91cc75-focus-2"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-3" for="chartlet-6880a2bcad91cc75-focus-3"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-4" for="chartlet-6880a2bcad91cc75-focus-4"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-5" for="chartlet-6880a2bcad91cc75-focus-5"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-6" for="chartlet-6880a2bcad91cc75-focus-6"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-7" for="chartlet-6880a2bcad91cc75-focus-7"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-8" for="chartlet-6880a2bcad91cc75-focus-8"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-9" for="chartlet-6880a2bcad91cc75-focus-9"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-hs-10" for="chartlet-6880a2bcad91cc75-focus-10"></label></div></div><div class="chartlet-variant-mobile"><div class="chartlet-stage"><svg xmlns="http://www.w3.org/2000/svg" width="468" height="1296" viewBox="0 0 468 1296" id="chartlet-6880a2bcad91cc75-m" role="img" aria-labelledby="chartlet-6880a2bcad91cc75-m-title chartlet-6880a2bcad91cc75-m-description" class="chartlet-root"><title id="chartlet-6880a2bcad91cc75-m-title">Tenant architecture: the sign-in and data path</title><desc id="chartlet-6880a2bcad91cc75-m-description">Architecture diagram with 11 components and 10 connections in 3 boundaries. Data center: Reverse proxy, Application network. Application network (in Data center): Web / BFF, Sign-in, Domain API, Data zone. Data zone (in Application network): Domain data, Audit log. Main path: Teacher, parent → Browser → Reverse proxy → Web / BFF → Domain API → Domain data. Most connected: Domain API (5 connections). Nothing leads into Teacher, parent and School records. Teacher, parent (person): connects to Browser (uses). Browser (frontend): connects to Reverse proxy (calls, via HTTPS). School records (external system): connects to Domain API (master data, via REST/JSON). Reverse proxy (security): connects to Web / BFF (forwards). Web / BFF (service): connects to Sign-in (signs in, via OIDC) and Domain API (queries, via user context). Sign-in (security): no outgoing connection. Domain API (service): connects to Domain data (reads, writes, via SQL/TLS), Audit log (logs) and SIEM / SOC (events, via Syslog). Domain data (database): connects to Backup (backs up). Audit log (database): no outgoing connection. Backup (storage): no outgoing connection. SIEM / SOC (external system): no outgoing connection. Emphasized: Sign-in. Emphasized path: Web / BFF → Domain API → Domain data. Legend: person, frontend, service, database, storage, security, external system, emphasized, solid line, dashed line, emphasized path.</desc><style>.chartlet-root{--chartlet-text:#172033;--chartlet-muted:#344054;--chartlet-grid:#d9dee8;--chartlet-zero:#667085;--chartlet-accent:#2563eb;--chartlet-background:#fff;--chartlet-color-1:#2563eb;--chartlet-color-2:#c2410c;--chartlet-color-3:#475569;--chartlet-color-4:#111827;max-width:100%;height:auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:var(--chartlet-text)}#chartlet-6880a2bcad91cc75-m .chartlet-title{font-size:22px;font-weight:650;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75-m .chartlet-label,#chartlet-6880a2bcad91cc75-m .chartlet-tick,#chartlet-6880a2bcad91cc75-m .chartlet-value,#chartlet-6880a2bcad91cc75-m .chartlet-value-inverse,#chartlet-6880a2bcad91cc75-m .chartlet-axis-title{font-size:12px;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75-m .chartlet-value,#chartlet-6880a2bcad91cc75-m .chartlet-value-inverse{font-weight:600}#chartlet-6880a2bcad91cc75-m .chartlet-value{fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75-m .chartlet-value-inverse{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75-m .chartlet-axis-title{font-weight:600}#chartlet-6880a2bcad91cc75-m .chartlet-grid{stroke:var(--chartlet-grid);stroke-width:1}#chartlet-6880a2bcad91cc75-m .chartlet-zero{stroke:var(--chartlet-zero);stroke-width:1.5}#chartlet-6880a2bcad91cc75-m .chartlet-bar,#chartlet-6880a2bcad91cc75-m .chartlet-point{fill:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75-m .chartlet-line{fill:none;stroke:var(--chartlet-accent);stroke-width:3;stroke-linecap:round;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-background{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75-m .chartlet-title-small{font-size:18px}.chartlet-root{--chartlet-node:#eef3fd;--chartlet-lane:#f7f9fc;--chartlet-lane-head:#eef1f6;--chartlet-shadow:rgba(16,24,40,.09);--chartlet-role-blue:#2563eb;--chartlet-role-blue-fill:#eef3fd;--chartlet-role-green:#15803d;--chartlet-role-green-fill:#ecfdf3;--chartlet-role-amber:#b45309;--chartlet-role-amber-fill:#fff7e8;--chartlet-role-teal:#0f766e;--chartlet-role-teal-fill:#e9faf7;--chartlet-role-violet:#6d28d9;--chartlet-role-violet-fill:#f3efff;--chartlet-role-gray:#667085;--chartlet-role-gray-fill:#f7f8fa;--chartlet-role-red:#b42318;--chartlet-role-red-fill:#fef3f2}.chartlet-root.chartlet-theme-dark{--chartlet-node:#1a2436;--chartlet-lane:#121925;--chartlet-lane-head:#18212f;--chartlet-shadow:rgba(0,0,0,.45);--chartlet-role-blue:#7ea6ff;--chartlet-role-blue-fill:#1a2436;--chartlet-role-green:#4ade80;--chartlet-role-green-fill:#132a1d;--chartlet-role-amber:#fbbf24;--chartlet-role-amber-fill:#2b2210;--chartlet-role-teal:#2dd4bf;--chartlet-role-teal-fill:#0f2a27;--chartlet-role-violet:#b69cff;--chartlet-role-violet-fill:#231c38;--chartlet-role-gray:#9aa7bd;--chartlet-role-gray-fill:#161d29;--chartlet-role-red:#f97066;--chartlet-role-red-fill:#2d1616}#chartlet-6880a2bcad91cc75-m .chartlet-role-blue{fill:var(--chartlet-role-blue-fill);stroke:var(--chartlet-role-blue)}#chartlet-6880a2bcad91cc75-m .chartlet-role-green{fill:var(--chartlet-role-green-fill);stroke:var(--chartlet-role-green)}#chartlet-6880a2bcad91cc75-m .chartlet-role-amber{fill:var(--chartlet-role-amber-fill);stroke:var(--chartlet-role-amber)}#chartlet-6880a2bcad91cc75-m .chartlet-role-teal{fill:var(--chartlet-role-teal-fill);stroke:var(--chartlet-role-teal)}#chartlet-6880a2bcad91cc75-m .chartlet-role-violet{fill:var(--chartlet-role-violet-fill);stroke:var(--chartlet-role-violet)}#chartlet-6880a2bcad91cc75-m .chartlet-role-gray{fill:var(--chartlet-role-gray-fill);stroke:var(--chartlet-role-gray)}#chartlet-6880a2bcad91cc75-m .chartlet-role-red{fill:var(--chartlet-role-red-fill);stroke:var(--chartlet-role-red)}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-rim{fill:none;stroke:var(--chartlet-role-teal);stroke-width:1.5}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-shadow{fill:var(--chartlet-shadow);stroke:none;rx:7px}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-chip{fill:var(--chartlet-background);stroke:var(--chartlet-grid);stroke-width:1;rx:4px}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-chip-text{font-size:12px;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-badge{fill:var(--chartlet-accent);stroke:var(--chartlet-background);stroke-width:2}#chartlet-6880a2bcad91cc75-m .chartlet-diagram-badge-text{font-size:10px;font-weight:700;fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75-m .chartlet-flow-node{stroke-width:1.5;stroke-linejoin:round;rx:7px}#chartlet-6880a2bcad91cc75-m .chartlet-flow-start{stroke-width:2}#chartlet-6880a2bcad91cc75-m .chartlet-flow-end{stroke-width:3}#chartlet-6880a2bcad91cc75-m .chartlet-flow-external{stroke-dasharray:5 3}#chartlet-6880a2bcad91cc75-m .chartlet-flow-inner{stroke:var(--chartlet-role-blue);stroke-width:1}#chartlet-6880a2bcad91cc75-m .chartlet-flow-label{font-size:13px;font-weight:600;fill:var(--chartlet-text)}#chartlet-6880a2bcad91cc75-m .chartlet-flow-sublabel{font-size:11px;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75-m .chartlet-flow-edge{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-flow-dashed{stroke-dasharray:5 4}#chartlet-6880a2bcad91cc75-m .chartlet-flow-dotted{stroke-dasharray:1 4;stroke-linecap:round}#chartlet-6880a2bcad91cc75-m .chartlet-flow-main{stroke:var(--chartlet-accent);stroke-width:2.2}#chartlet-6880a2bcad91cc75-m .chartlet-flow-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero);stroke-width:1;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-flow-main-head{fill:var(--chartlet-accent);stroke:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75-m .chartlet-flow-lane{fill:var(--chartlet-lane);stroke:var(--chartlet-grid);stroke-width:1}#chartlet-6880a2bcad91cc75-m .chartlet-flow-lane-alt{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75-m .chartlet-flow-lane-head{fill:var(--chartlet-lane-head);stroke:none}#chartlet-6880a2bcad91cc75-m .chartlet-flow-lane-label{font-size:11px;font-weight:650;fill:var(--chartlet-muted);letter-spacing:.04em}#chartlet-6880a2bcad91cc75-m .chartlet-flow-group{fill:var(--chartlet-accent);fill-opacity:.04;stroke:var(--chartlet-zero);stroke-width:1;stroke-dasharray:4 3;rx:10px}#chartlet-6880a2bcad91cc75-m .chartlet-flow-group-label{font-size:11px;font-weight:600;fill:var(--chartlet-muted)}#chartlet-6880a2bcad91cc75-m .chartlet-arch-head{fill:var(--chartlet-role-violet-fill);stroke:var(--chartlet-role-violet);stroke-width:1.5}#chartlet-6880a2bcad91cc75-m .chartlet-arch-dot{fill:var(--chartlet-role-blue);stroke:none}#chartlet-6880a2bcad91cc75-m .chartlet-emph .chartlet-flow-node,#chartlet-6880a2bcad91cc75-m .chartlet-emph .chartlet-seq-box{stroke:var(--chartlet-accent);stroke-width:3.5;fill:var(--chartlet-accent);fill-opacity:.14}#chartlet-6880a2bcad91cc75-m .chartlet-emph .chartlet-diagram-shadow{fill:none}#chartlet-6880a2bcad91cc75-m .chartlet-emph .chartlet-seq-actor,#chartlet-6880a2bcad91cc75-m .chartlet-emph .chartlet-seq-actor-head{stroke:var(--chartlet-accent);stroke-width:2.6}#chartlet-6880a2bcad91cc75-m .chartlet-emph-ring{fill:none;stroke:var(--chartlet-accent);stroke-width:3}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-flow-edge,#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-seq-message{stroke:var(--chartlet-accent);stroke-width:3.2}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-flow-head,#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-seq-head{fill:var(--chartlet-accent);stroke:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-flow-hollow{fill:var(--chartlet-background)}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-flow-solid{fill:var(--chartlet-accent)}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-flow-open,#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-seq-head-open{stroke:var(--chartlet-accent);stroke-width:2.4}#chartlet-6880a2bcad91cc75-m .chartlet-emph-edge .chartlet-diagram-chip-text{font-weight:700}#chartlet-6880a2bcad91cc75-m .chartlet-emph-quiet .chartlet-flow-main{stroke:var(--chartlet-zero);stroke-width:1.4}#chartlet-6880a2bcad91cc75-m .chartlet-emph-quiet .chartlet-flow-main-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero)}#chartlet-6880a2bcad91cc75-m .chartlet-legend-line{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-legend-dashed{stroke-dasharray:5 4}#chartlet-6880a2bcad91cc75-m .chartlet-legend-dotted{stroke-dasharray:1 4;stroke-linecap:round}#chartlet-6880a2bcad91cc75-m .chartlet-legend-main{stroke:var(--chartlet-accent);stroke-width:2.2}#chartlet-6880a2bcad91cc75-m .chartlet-legend-path{stroke:var(--chartlet-accent);stroke-width:3.2}#chartlet-6880a2bcad91cc75-m .chartlet-legend-head{fill:var(--chartlet-zero);stroke:var(--chartlet-zero);stroke-width:1;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-legend-open{fill:none;stroke:var(--chartlet-zero);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}#chartlet-6880a2bcad91cc75-m .chartlet-legend-emph{fill:var(--chartlet-accent);fill-opacity:.14;stroke:var(--chartlet-accent);stroke-width:3.5;rx:4px}#chartlet-6880a2bcad91cc75-m .chartlet-legend-text{font-size:12px;fill:var(--chartlet-muted)}</style><rect x="12" y="304" width="384" height="732" class="chartlet-flow-group"/><text x="20" y="317" text-anchor="start" class="chartlet-flow-group-label">Data center</text><rect x="24" y="468" width="360" height="556" class="chartlet-flow-group"/><text x="32" y="481" text-anchor="start" class="chartlet-flow-group-label">Application network</text><rect x="140" y="892" width="232" height="120" class="chartlet-flow-group"/><text x="148" y="905" text-anchor="start" class="chartlet-flow-group-label">Data zone</text><g class="chartlet-f chartlet-f-0 chartlet-f-1 chartlet-emph-quiet"><polyline points="204,88 204,156" class="chartlet-flow-edge chartlet-flow-main"><title>Teacher, parent → Browser: uses</title></polyline><polyline points="200.5,156 204,164 207.5,156 200.5,156" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-1 chartlet-f-2 chartlet-emph-quiet"><polyline points="204,212 204,324" class="chartlet-flow-edge chartlet-flow-main"><title>Browser → Reverse proxy: calls [HTTPS]</title></polyline><polyline points="200.5,324 204,332 207.5,324 200.5,324" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-2 chartlet-f-3 chartlet-emph-quiet"><polyline points="204,404 204,488" class="chartlet-flow-edge chartlet-flow-main"><title>Reverse proxy → Web / BFF: forwards</title></polyline><polyline points="200.5,488 204,496 207.5,488 200.5,488" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-3 chartlet-f-4 chartlet-emph-quiet"><polyline points="180,552 180,616.125 88,616.125 88,636" class="chartlet-flow-edge"><title>Web / BFF → Sign-in: signs in [OIDC]</title></polyline><polyline points="84.5,636 88,644 91.5,636 84.5,636" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-3 chartlet-f-5 chartlet-emph-edge"><polyline points="204,552 204,644" class="chartlet-flow-edge chartlet-flow-main"><title>Web / BFF → Domain API: queries [user context]</title></polyline><polyline points="200.5,644 204,652 207.5,644 200.5,644" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-6 chartlet-emph-edge"><polyline points="204,708 204,912" class="chartlet-flow-edge chartlet-flow-main"><title>Domain API → Domain data: reads, writes [SQL/TLS]</title></polyline><polyline points="200.5,912 204,920 207.5,912 200.5,912" class="chartlet-flow-head chartlet-flow-main-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-7 chartlet-emph-quiet"><polyline points="224,708 224,860.281 316,860.281 316,920" class="chartlet-flow-edge"><title>Domain API → Audit log: logs</title></polyline><polyline points="312.5,920 316,928 319.5,920 312.5,920" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-9 chartlet-emph-quiet"><polyline points="316,224 316,616.125 228,616.125 228,644" class="chartlet-flow-edge"><title>School records → Domain API: master data [REST/JSON]</title></polyline><polyline points="224.5,644 228,652 231.5,644 224.5,644" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-5 chartlet-f-10 chartlet-emph-quiet"><polyline points="244,708 244,847.456 428,847.456 428,1086.569 404,1086.569 404,1112" class="chartlet-flow-edge chartlet-flow-dashed"><title>Domain API → SIEM / SOC: events [Syslog]</title></polyline><polyline points="400.5,1112 404,1120 407.5,1112 400.5,1112" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-f-6 chartlet-f-8 chartlet-emph-quiet"><polyline points="204,1000 204,1086.569 180,1086.569 180,1104" class="chartlet-flow-edge chartlet-flow-dashed"><title>Domain data → Backup: backs up</title></polyline><polyline points="176.5,1104 180,1112 183.5,1104 176.5,1104" class="chartlet-flow-head"/></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2"></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-3"></g><g class="chartlet-f chartlet-n-3 chartlet-f-2 chartlet-f-3 chartlet-f-4 chartlet-f-5"></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4"></g><g class="chartlet-f chartlet-n-5 chartlet-f-3 chartlet-f-5 chartlet-f-6 chartlet-f-7 chartlet-f-9 chartlet-f-10"></g><g class="chartlet-f chartlet-n-6 chartlet-f-5 chartlet-f-6 chartlet-f-8"></g><g class="chartlet-f chartlet-n-7 chartlet-f-5 chartlet-f-7"></g><g class="chartlet-f chartlet-n-8 chartlet-f-6 chartlet-f-8"></g><g class="chartlet-f chartlet-n-9 chartlet-f-5 chartlet-f-9"></g><g class="chartlet-f chartlet-n-10 chartlet-f-5 chartlet-f-10"></g><g class="chartlet-f chartlet-f-0 chartlet-f-1 chartlet-emph-quiet"><rect x="208" y="93" width="36.88" height="21" class="chartlet-diagram-chip"/><text x="213" y="107" text-anchor="start" class="chartlet-diagram-chip-text">uses</text></g><g class="chartlet-f chartlet-f-1 chartlet-f-2 chartlet-emph-quiet"><rect x="208" y="227" width="63.64" height="36" class="chartlet-diagram-chip"/><text x="213" y="241" text-anchor="start" class="chartlet-diagram-chip-text">calls</text><text x="213" y="256" text-anchor="start" class="chartlet-diagram-chip-text">[HTTPS]</text></g><g class="chartlet-f chartlet-f-2 chartlet-f-3 chartlet-emph-quiet"><rect x="208" y="409" width="67.84" height="21" class="chartlet-diagram-chip"/><text x="213" y="423" text-anchor="start" class="chartlet-diagram-chip-text">forwards</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-4 chartlet-emph-quiet"><rect x="121.36" y="557" width="54.64" height="36" class="chartlet-diagram-chip"/><text x="171" y="571" text-anchor="end" class="chartlet-diagram-chip-text">signs in</text><text x="171" y="586" text-anchor="end" class="chartlet-diagram-chip-text">[OIDC]</text></g><g class="chartlet-f chartlet-f-3 chartlet-f-5 chartlet-emph-edge"><rect x="208" y="557" width="101.2" height="36" class="chartlet-diagram-chip"/><text x="213" y="571" text-anchor="start" class="chartlet-diagram-chip-text">queries</text><text x="213" y="586" text-anchor="start" class="chartlet-diagram-chip-text">[user context]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-6 chartlet-emph-edge"><rect x="107.68" y="719" width="92.32" height="36" class="chartlet-diagram-chip"/><text x="195" y="733" text-anchor="end" class="chartlet-diagram-chip-text">reads, writes</text><text x="195" y="748" text-anchor="end" class="chartlet-diagram-chip-text">[SQL/TLS]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-7 chartlet-emph-quiet"><rect x="253.12" y="864.281" width="33.76" height="21" class="chartlet-diagram-chip"/><text x="270" y="878.281" text-anchor="middle" class="chartlet-diagram-chip-text">logs</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-9 chartlet-emph-quiet"><rect x="320" y="229" width="94.48" height="36" class="chartlet-diagram-chip"/><text x="325" y="243" text-anchor="start" class="chartlet-diagram-chip-text">master data</text><text x="325" y="258" text-anchor="start" class="chartlet-diagram-chip-text">[REST/JSON]</text></g><g class="chartlet-f chartlet-f-5 chartlet-f-10 chartlet-emph-quiet"><rect x="248" y="713" width="61.96" height="36" class="chartlet-diagram-chip"/><text x="253" y="727" text-anchor="start" class="chartlet-diagram-chip-text">events</text><text x="253" y="742" text-anchor="start" class="chartlet-diagram-chip-text">[Syslog]</text></g><g class="chartlet-f chartlet-f-6 chartlet-f-8 chartlet-emph-quiet"><rect x="208" y="1039" width="60.88" height="21" class="chartlet-diagram-chip"/><text x="213" y="1053" text-anchor="start" class="chartlet-diagram-chip-text">backs up</text></g><g class="chartlet-f chartlet-n-0 chartlet-f-0 chartlet-f-1"><rect x="160" y="18.5" width="88" height="72" class="chartlet-diagram-shadow"/><rect x="160" y="16" width="88" height="72" class="chartlet-flow-node chartlet-role-violet"><title>Teacher, parent</title></rect><circle cx="204" cy="27" r="6" class="chartlet-arch-head"/><text x="204" y="56.5" text-anchor="middle" class="chartlet-flow-label">Teacher,</text><text x="204" y="72.5" text-anchor="middle" class="chartlet-flow-label">parent</text></g><g class="chartlet-f chartlet-n-1 chartlet-f-0 chartlet-f-1 chartlet-f-2"><rect x="160" y="166.5" width="88" height="48" class="chartlet-diagram-shadow"/><rect x="160" y="164" width="88" height="48" class="chartlet-flow-node chartlet-role-blue"><title>Browser</title></rect><line x1="160" y1="176" x2="248" y2="176" class="chartlet-flow-inner"/><circle cx="168" cy="170" r="1.8" class="chartlet-arch-dot"/><circle cx="174" cy="170" r="1.8" class="chartlet-arch-dot"/><circle cx="180" cy="170" r="1.8" class="chartlet-arch-dot"/><text x="204" y="198.5" text-anchor="middle" class="chartlet-flow-label">Browser</text></g><g class="chartlet-f chartlet-n-2 chartlet-f-1 chartlet-f-2 chartlet-f-3"><polyline points="144,334.5 264,334.5 264,377.7 204,406.5 144,377.7 144,334.5" class="chartlet-diagram-shadow"/><polyline points="144,332 264,332 264,375.2 204,404 144,375.2 144,332" class="chartlet-flow-node chartlet-role-red"><title>Reverse proxy – TLS 1.3, WAF</title></polyline><text x="204" y="359.5" text-anchor="middle" class="chartlet-flow-label">Reverse proxy</text><text x="204" y="373.5" text-anchor="middle" class="chartlet-flow-sublabel">TLS 1.3, WAF</text></g><g class="chartlet-f chartlet-n-3 chartlet-f-2 chartlet-f-3 chartlet-f-4 chartlet-f-5"><rect x="156" y="498.5" width="96" height="56" class="chartlet-diagram-shadow"/><rect x="156" y="496" width="96" height="56" class="chartlet-flow-node chartlet-role-blue"><title>Web / BFF – SvelteKit</title></rect><text x="204" y="521.5" text-anchor="middle" class="chartlet-flow-label">Web / BFF</text><text x="204" y="535.5" text-anchor="middle" class="chartlet-flow-sublabel">SvelteKit</text></g><g class="chartlet-f chartlet-n-4 chartlet-f-3 chartlet-f-4 chartlet-emph"><polyline points="36,646.5 140,646.5 140,689.7 88,718.5 36,689.7 36,646.5" class="chartlet-diagram-shadow"/><polyline points="36,644 140,644 140,687.2 88,716 36,687.2 36,644" class="chartlet-flow-node chartlet-role-red"><title>Sign-in – Keycloak, OIDC</title></polyline><text x="88" y="671.5" text-anchor="middle" class="chartlet-flow-label">Sign-in</text><text x="88" y="685.5" text-anchor="middle" class="chartlet-flow-sublabel">Keycloak, OIDC</text></g><g class="chartlet-f chartlet-n-5 chartlet-f-3 chartlet-f-5 chartlet-f-6 chartlet-f-7 chartlet-f-9 chartlet-f-10"><rect x="156" y="654.5" width="96" height="56" class="chartlet-diagram-shadow"/><rect x="156" y="652" width="96" height="56" class="chartlet-flow-node chartlet-role-blue"><title>Domain API – Rust</title></rect><text x="204" y="677.5" text-anchor="middle" class="chartlet-flow-label">Domain API</text><text x="204" y="691.5" text-anchor="middle" class="chartlet-flow-sublabel">Rust</text></g><g class="chartlet-f chartlet-n-6 chartlet-f-5 chartlet-f-6 chartlet-f-8"><polyline points="152,927.5 152,997.5 153.772,998.794 158.967,1000 167.23,1001.036 178,1001.83 190.541,1002.33 204,1002.5 217.459,1002.33 230,1001.83 240.77,1001.036 249.033,1000 254.228,998.794 256,997.5 256,927.5 254.228,926.206 249.033,925 240.77,923.964 230,923.17 217.459,922.67 204,922.5 190.541,922.67 178,923.17 167.23,923.964 158.967,925 153.772,926.206 152,927.5 152,927.5" class="chartlet-diagram-shadow"/><polyline points="152,925 152,995 153.772,996.294 158.967,997.5 167.23,998.536 178,999.33 190.541,999.83 204,1000 217.459,999.83 230,999.33 240.77,998.536 249.033,997.5 254.228,996.294 256,995 256,925 254.228,923.706 249.033,922.5 240.77,921.464 230,920.67 217.459,920.17 204,920 190.541,920.17 178,920.67 167.23,921.464 158.967,922.5 153.772,923.706 152,925 152,925" class="chartlet-flow-node chartlet-role-teal"><title>Domain data – Postgres, RLS per tenant</title></polyline><polyline points="152,925 153.772,926.294 158.967,927.5 167.23,928.536 178,929.33 190.541,929.83 204,930 217.459,929.83 230,929.33 240.77,928.536 249.033,927.5 254.228,926.294 256,925" class="chartlet-diagram-rim"/><text x="204" y="953.5" text-anchor="middle" class="chartlet-flow-label">Domain data</text><text x="204" y="967.5" text-anchor="middle" class="chartlet-flow-sublabel">Postgres, RLS</text><text x="204" y="981.5" text-anchor="middle" class="chartlet-flow-sublabel">per tenant</text></g><g class="chartlet-f chartlet-n-7 chartlet-f-5 chartlet-f-7"><polyline points="272,935.5 272,989.5 273.499,990.794 277.895,992 284.887,993.036 294,993.83 304.612,994.33 316,994.5 327.388,994.33 338,993.83 347.113,993.036 354.105,992 358.501,990.794 360,989.5 360,935.5 358.501,934.206 354.105,933 347.113,931.964 338,931.17 327.388,930.67 316,930.5 304.612,930.67 294,931.17 284.887,931.964 277.895,933 273.499,934.206 272,935.5 272,935.5" class="chartlet-diagram-shadow"/><polyline points="272,933 272,987 273.499,988.294 277.895,989.5 284.887,990.536 294,991.33 304.612,991.83 316,992 327.388,991.83 338,991.33 347.113,990.536 354.105,989.5 358.501,988.294 360,987 360,933 358.501,931.706 354.105,930.5 347.113,929.464 338,928.67 327.388,928.17 316,928 304.612,928.17 294,928.67 284.887,929.464 277.895,930.5 273.499,931.706 272,933 272,933" class="chartlet-flow-node chartlet-role-teal"><title>Audit log – append only</title></polyline><polyline points="272,933 273.499,934.294 277.895,935.5 284.887,936.536 294,937.33 304.612,937.83 316,938 327.388,937.83 338,937.33 347.113,936.536 354.105,935.5 358.501,934.294 360,933" class="chartlet-diagram-rim"/><text x="316" y="960.5" text-anchor="middle" class="chartlet-flow-label">Audit log</text><text x="316" y="974.5" text-anchor="middle" class="chartlet-flow-sublabel">append only</text></g><g class="chartlet-f chartlet-n-8 chartlet-f-6 chartlet-f-8"><polyline points="128,1114.5 232,1114.5 224,1170.5 136,1170.5 128,1114.5" class="chartlet-diagram-shadow"/><polyline points="128,1112 232,1112 224,1168 136,1168 128,1112" class="chartlet-flow-node chartlet-role-teal"><title>Backup – second site</title></polyline><text x="180" y="1137.5" text-anchor="middle" class="chartlet-flow-label">Backup</text><text x="180" y="1151.5" text-anchor="middle" class="chartlet-flow-sublabel">second site</text></g><g class="chartlet-f chartlet-n-9 chartlet-f-5 chartlet-f-9"><rect x="264" y="154.5" width="104" height="72" class="chartlet-diagram-shadow"/><rect x="264" y="152" width="104" height="72" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"><title>School records – legacy system</title></rect><text x="316" y="177.5" text-anchor="middle" class="chartlet-flow-label">School</text><text x="316" y="193.5" text-anchor="middle" class="chartlet-flow-label">records</text><text x="316" y="207.5" text-anchor="middle" class="chartlet-flow-sublabel">legacy system</text></g><g class="chartlet-f chartlet-n-10 chartlet-f-5 chartlet-f-10"><rect x="352" y="1122.5" width="104" height="40" class="chartlet-diagram-shadow"/><rect x="352" y="1120" width="104" height="40" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"><title>SIEM / SOC</title></rect><text x="404" y="1144.5" text-anchor="middle" class="chartlet-flow-label">SIEM / SOC</text></g><g class="chartlet-legend-strip"><rect x="24" y="1198.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="24" y="1196" width="26" height="16" class="chartlet-flow-node chartlet-role-violet"/><circle cx="37" cy="1207" r="6" class="chartlet-arch-head"/><text x="58" y="1208" text-anchor="start" class="chartlet-legend-text">Person</text><rect x="121.64" y="1198.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="121.64" y="1196" width="26" height="16" class="chartlet-flow-node chartlet-role-blue"/><line x1="121.64" y1="1208" x2="147.64" y2="1208" class="chartlet-flow-inner"/><circle cx="129.64" cy="1202" r="1.8" class="chartlet-arch-dot"/><circle cx="135.64" cy="1202" r="1.8" class="chartlet-arch-dot"/><circle cx="141.64" cy="1202" r="1.8" class="chartlet-arch-dot"/><text x="155.64" y="1208" text-anchor="start" class="chartlet-legend-text">Frontend</text><rect x="232.72" y="1198.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="232.72" y="1196" width="26" height="16" class="chartlet-flow-node chartlet-role-blue"/><text x="266.72" y="1208" text-anchor="start" class="chartlet-legend-text">Service</text><polyline points="333.96,1203.5 333.96,1209.5 334.403,1210.794 335.702,1212 337.768,1213.036 340.46,1213.83 343.595,1214.33 346.96,1214.5 350.325,1214.33 353.46,1213.83 356.152,1213.036 358.218,1212 359.517,1210.794 359.96,1209.5 359.96,1203.5 359.517,1202.206 358.218,1201 356.152,1199.964 353.46,1199.17 350.325,1198.67 346.96,1198.5 343.595,1198.67 340.46,1199.17 337.768,1199.964 335.702,1201 334.403,1202.206 333.96,1203.5 333.96,1203.5" class="chartlet-diagram-shadow"/><polyline points="333.96,1201 333.96,1207 334.403,1208.294 335.702,1209.5 337.768,1210.536 340.46,1211.33 343.595,1211.83 346.96,1212 350.325,1211.83 353.46,1211.33 356.152,1210.536 358.218,1209.5 359.517,1208.294 359.96,1207 359.96,1201 359.517,1199.706 358.218,1198.5 356.152,1197.464 353.46,1196.67 350.325,1196.17 346.96,1196 343.595,1196.17 340.46,1196.67 337.768,1197.464 335.702,1198.5 334.403,1199.706 333.96,1201 333.96,1201" class="chartlet-flow-node chartlet-role-teal"/><polyline points="333.96,1201 334.403,1202.294 335.702,1203.5 337.768,1204.536 340.46,1205.33 343.595,1205.83 346.96,1206 350.325,1205.83 353.46,1205.33 356.152,1204.536 358.218,1203.5 359.517,1202.294 359.96,1201" class="chartlet-diagram-rim"/><text x="367.96" y="1208" text-anchor="start" class="chartlet-legend-text">Database</text><polyline points="24,1222.5 50,1222.5 42,1238.5 32,1238.5 24,1222.5" class="chartlet-diagram-shadow"/><polyline points="24,1220 50,1220 42,1236 32,1236 24,1220" class="chartlet-flow-node chartlet-role-teal"/><text x="58" y="1232" text-anchor="start" class="chartlet-legend-text">Storage</text><polyline points="128.36,1222.5 154.36,1222.5 154.36,1232.1 141.36,1238.5 128.36,1232.1 128.36,1222.5" class="chartlet-diagram-shadow"/><polyline points="128.36,1220 154.36,1220 154.36,1229.6 141.36,1236 128.36,1229.6 128.36,1220" class="chartlet-flow-node chartlet-role-red"/><text x="162.36" y="1232" text-anchor="start" class="chartlet-legend-text">Security</text><rect x="236.32" y="1222.5" width="26" height="16" class="chartlet-diagram-shadow"/><rect x="236.32" y="1220" width="26" height="16" class="chartlet-flow-node chartlet-flow-external chartlet-role-gray"/><text x="270.32" y="1232" text-anchor="start" class="chartlet-legend-text">External system</text><rect x="24" y="1244" width="26" height="16" class="chartlet-legend-emph"/><text x="58" y="1256" text-anchor="start" class="chartlet-legend-text">Emphasized</text><line x1="149.48" y1="1252" x2="175.48" y2="1252" class="chartlet-legend-line"/><text x="183.48" y="1256" text-anchor="start" class="chartlet-legend-text">Solid line</text><line x1="258.64" y1="1252" x2="284.64" y2="1252" class="chartlet-legend-line chartlet-legend-dashed"/><text x="292.64" y="1256" text-anchor="start" class="chartlet-legend-text">Dashed line</text><line x1="24" y1="1276" x2="50" y2="1276" class="chartlet-legend-line chartlet-legend-path"/><text x="58" y="1280" text-anchor="start" class="chartlet-legend-text">Emphasized path</text></g></svg><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-0" for="chartlet-6880a2bcad91cc75-focus-0"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-1" for="chartlet-6880a2bcad91cc75-focus-1"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-2" for="chartlet-6880a2bcad91cc75-focus-2"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-3" for="chartlet-6880a2bcad91cc75-focus-3"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-4" for="chartlet-6880a2bcad91cc75-focus-4"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-5" for="chartlet-6880a2bcad91cc75-focus-5"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-6" for="chartlet-6880a2bcad91cc75-focus-6"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-7" for="chartlet-6880a2bcad91cc75-focus-7"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-8" for="chartlet-6880a2bcad91cc75-focus-8"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-9" for="chartlet-6880a2bcad91cc75-focus-9"></label><label class="chartlet-hotspot" id="chartlet-6880a2bcad91cc75-m-hs-10" for="chartlet-6880a2bcad91cc75-focus-10"></label></div></div><p class="chartlet-source">Source: Illustrative reference architecture</p><details class="chartlet-data" data-viz-text><summary>Show chart data</summary><table><caption>Data for Tenant architecture: the sign-in and data path</caption><thead><tr><th scope="col">Component</th><th scope="col">Kind</th><th scope="col">Boundary</th><th scope="col">Connects to</th><th scope="col">Emphasis</th></tr></thead><tbody><tr><th scope="row">Teacher, parent</th><td>person</td><td></td><td>Browser (uses)</td><td></td></tr><tr><th scope="row">Browser</th><td>frontend</td><td></td><td>Reverse proxy (calls, via HTTPS)</td><td></td></tr><tr><th scope="row">School records</th><td>external system</td><td></td><td>Domain API (master data, via REST/JSON)</td><td></td></tr><tr><th scope="row">Reverse proxy</th><td>security</td><td>Data center</td><td>Web / BFF (forwards)</td><td></td></tr><tr><th scope="row">Web / BFF</th><td>service</td><td>Data center › Application network</td><td>Sign-in (signs in, via OIDC), Domain API (queries, via user context)</td><td>on emphasized path</td></tr><tr><th scope="row">Sign-in</th><td>security</td><td>Data center › Application network</td><td></td><td>emphasized</td></tr><tr><th scope="row">Domain API</th><td>service</td><td>Data center › Application network</td><td>Domain data (reads, writes, via SQL/TLS), Audit log (logs), SIEM / SOC (events, via Syslog)</td><td>on emphasized path</td></tr><tr><th scope="row">Domain data</th><td>database</td><td>Data center › Application network › Data zone</td><td>Backup (backs up)</td><td>on emphasized path</td></tr><tr><th scope="row">Audit log</th><td>database</td><td>Data center › Application network › Data zone</td><td></td><td></td></tr><tr><th scope="row">Backup</th><td>storage</td><td></td><td></td><td></td></tr><tr><th scope="row">SIEM / SOC</th><td>external system</td><td></td><td></td><td></td></tr></tbody></table></details></figure></div>