Files
query-orchestration/docs/ai.generated/architecture-diagram.svg
T

640 lines
50 KiB
XML
Raw Normal View History

<svg width="1800" height="1120" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style>
text {
font-family: -apple-system, 'Helvetica Neue', 'Arial', sans-serif;
font-variant-ligatures: none;
}
</style>
<!-- Arrow marker -->
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#94A3B8"/>
</marker>
<marker id="arrowhead-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#3B82F6"/>
</marker>
<!-- Column header gradients -->
<linearGradient id="grad-rest" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#1D4ED8;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#2563EB;stop-opacity:1"/>
</linearGradient>
<linearGradient id="grad-mid" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#6D28D9;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#7C3AED;stop-opacity:1"/>
</linearGradient>
<linearGradient id="grad-ctrl" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#BE185D;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#DB2777;stop-opacity:1"/>
</linearGradient>
<linearGradient id="grad-svc" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#047857;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#059669;stop-opacity:1"/>
</linearGradient>
<linearGradient id="grad-repo" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#B45309;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#D97706;stop-opacity:1"/>
</linearGradient>
<linearGradient id="grad-db" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#B91C1C;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#DC2626;stop-opacity:1"/>
</linearGradient>
<!-- Title gradient -->
<linearGradient id="grad-title" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#0F172A;stop-opacity:1"/>
<stop offset="50%" style="stop-color:#1E3A5F;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#0F172A;stop-opacity:1"/>
</linearGradient>
<!-- Subtle card shadow -->
<filter id="card-shadow" x="-3%" y="-3%" width="106%" height="112%">
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-color="#0000001A"/>
</filter>
<filter id="col-shadow" x="-2%" y="-1%" width="104%" height="102%">
<feDropShadow dx="0" dy="3" stdDeviation="6" flood-color="#00000020"/>
</filter>
<!-- Column background tints -->
<linearGradient id="col-bg-rest" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#EFF6FF;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<linearGradient id="col-bg-mid" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#F5F3FF;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<linearGradient id="col-bg-ctrl" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#FDF2F8;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<linearGradient id="col-bg-svc" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#ECFDF5;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<linearGradient id="col-bg-repo" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#FFFBEB;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<linearGradient id="col-bg-db" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#FEF2F2;stop-opacity:1"/>
<stop offset="100%" style="stop-color:#F8FAFC;stop-opacity:1"/>
</linearGradient>
<!-- Dot grid pattern -->
<pattern id="dot-grid" x="0" y="0" width="24" height="24" patternUnits="userSpaceOnUse">
<circle cx="1.5" cy="1.5" r="1" fill="#CBD5E1" opacity="0.5"/>
</pattern>
</defs>
<!-- Canvas background -->
<rect width="1800" height="1120" fill="#F1F5F9"/>
<rect width="1800" height="1120" fill="url(#dot-grid)"/>
<!-- Title bar -->
<rect x="0" y="0" width="1800" height="78" fill="url(#grad-title)"/>
<text x="900" y="34" text-anchor="middle" font-size="22" font-weight="700" fill="#FFFFFF" letter-spacing="1.5">QUERY ORCHESTRATION API</text>
<text x="900" y="58" text-anchor="middle" font-size="13" font-weight="400" fill="#94A3B8" letter-spacing="0.5">Software Architecture · Request Flow: Left → Right</text>
<!-- Flow direction label strip -->
<rect x="0" y="78" width="1800" height="28" fill="#0F172A" opacity="0.88"/>
<text x="154" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#93C5FD" letter-spacing="1">HTTP ENDPOINTS</text>
<text x="440" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#C4B5FD" letter-spacing="1">MIDDLEWARE</text>
<text x="710" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#F9A8D4" letter-spacing="1">CONTROLLERS</text>
<text x="1010" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#6EE7B7" letter-spacing="1">SERVICE LAYER</text>
<text x="1320" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#FCD34D" letter-spacing="1">REPOSITORY</text>
<text x="1620" y="96" text-anchor="middle" font-size="10.5" font-weight="600" fill="#FCA5A5" letter-spacing="1">DATABASE</text>
<!-- Arrow dividers in label strip -->
<polygon points="281,82 295,92 281,102" fill="#475569"/>
<polygon points="565,82 579,92 565,102" fill="#475569"/>
<polygon points="833,82 847,92 833,102" fill="#475569"/>
<polygon points="1133,82 1147,92 1133,102" fill="#475569"/>
<polygon points="1440,82 1454,92 1440,102" fill="#475569"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 1: REST HTTP ENDPOINTS -->
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- Column background -->
<rect x="8" y="110" width="285" height="700" rx="10" fill="url(#col-bg-rest)" stroke="#BFDBFE" stroke-width="1.5" filter="url(#col-shadow)"/>
<!-- Column header -->
<rect x="8" y="110" width="285" height="52" rx="10" fill="url(#grad-rest)"/>
<rect x="8" y="140" width="285" height="22" fill="url(#grad-rest)"/>
<text x="150.5" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">REST HTTP Endpoints</text>
<text x="150.5" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#BFDBFE">Echo v4 · OpenAPI-first</text>
<!-- Items -->
<!-- Item 1 -->
<rect x="18" y="174" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="174" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="190" font-size="11.5" font-weight="600" fill="#1E3A8A">Client Management</text>
<text x="33" y="207" font-size="10" fill="#64748B">GET/POST /client · PATCH/DELETE /client/{id}</text>
<!-- Item 2 -->
<rect x="18" y="222" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="222" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="238" font-size="11.5" font-weight="600" fill="#1E3A8A">Document Operations</text>
<text x="33" y="255" font-size="10" fill="#64748B">Upload · Download (S3 stream) · Batch</text>
<!-- Item 3 -->
<rect x="18" y="270" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="270" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="286" font-size="11.5" font-weight="600" fill="#1E3A8A">Folders &amp; Labels</text>
<text x="33" y="303" font-size="10" fill="#64748B">/folders · /documents/{id}/labels</text>
<!-- Item 4 -->
<rect x="18" y="318" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="318" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="334" font-size="11.5" font-weight="600" fill="#1E3A8A">Field Extractions</text>
<text x="33" y="351" font-size="10" fill="#64748B">/field-extractions · version · history</text>
<!-- Item 5 -->
<rect x="18" y="366" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="366" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="382" font-size="11.5" font-weight="600" fill="#1E3A8A">Custom Schemas</text>
<text x="33" y="399" font-size="10" fill="#64748B">/super-admin/custom-schemas · RBAC gated</text>
<!-- Item 6 -->
<rect x="18" y="414" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="414" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="430" font-size="11.5" font-weight="600" fill="#1E3A8A">Custom Metadata</text>
<text x="33" y="447" font-size="10" fill="#64748B">/custom-metadata · version · history</text>
<!-- Item 7 -->
<rect x="18" y="462" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="462" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="478" font-size="11.5" font-weight="600" fill="#1E3A8A">Export Management</text>
<text x="33" y="495" font-size="10" fill="#64748B">POST /client/{id}/export · GET /export/{id}</text>
<!-- Item 8 -->
<rect x="18" y="510" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="510" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="526" font-size="11.5" font-weight="600" fill="#1E3A8A">EULA Administration</text>
<text x="33" y="543" font-size="10" fill="#64748B">/eula · /admin/eula · agreements</text>
<!-- Item 9 -->
<rect x="18" y="558" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="558" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="574" font-size="11.5" font-weight="600" fill="#1E3A8A">User &amp; Identity</text>
<text x="33" y="591" font-size="10" fill="#64748B">/admin/users · /identity · /home</text>
<!-- Item 10 -->
<rect x="18" y="606" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="606" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="622" font-size="11.5" font-weight="600" fill="#1E3A8A">Auth &amp; UI Settings</text>
<text x="33" y="639" font-size="10" fill="#64748B">/login · /logout · /ui-settings</text>
<!-- Item 11 -->
<rect x="18" y="654" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="654" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="670" font-size="11.5" font-weight="600" fill="#1E3A8A">Collector Configuration</text>
<text x="33" y="687" font-size="10" fill="#64748B">GET/PATCH /client/{id}/collector</text>
<!-- Item 12 -->
<rect x="18" y="702" width="265" height="43" rx="6" fill="#FFFFFF" stroke="#DBEAFE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="18" y="702" width="4" height="43" rx="2" fill="#2563EB"/>
<text x="33" y="718" font-size="11.5" font-weight="600" fill="#1E3A8A">Health &amp; Metrics</text>
<text x="33" y="735" font-size="10" fill="#64748B">GET /health · GET /metrics (Prometheus)</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ARROW 1 → 2 -->
<!-- ═══════════════════════════════════════════════════════════ -->
<line x1="293" y1="460" x2="302" y2="460" stroke="#94A3B8" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 2: MIDDLEWARE STACK -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="302" y="110" width="260" height="700" rx="10" fill="url(#col-bg-mid)" stroke="#DDD6FE" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="302" y="110" width="260" height="52" rx="10" fill="url(#grad-mid)"/>
<rect x="302" y="140" width="260" height="22" fill="url(#grad-mid)"/>
<text x="432" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">Middleware Stack</text>
<text x="432" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#DDD6FE">internal/server/api/listener.go</text>
<!-- Middleware items (ordered) -->
<rect x="312" y="174" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="174" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="186" font-size="10.5" font-weight="600" fill="#4C1D95">Config Injection</text>
<text x="326" y="200" font-size="9.5" fill="#64748B">Injects runtime configuration into context</text>
<rect x="312" y="213" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="213" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="225" font-size="10.5" font-weight="600" fill="#4C1D95">Prometheus Metrics</text>
<text x="326" y="239" font-size="9.5" fill="#64748B">HTTP metrics collection · /metrics</text>
<rect x="312" y="252" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="252" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="264" font-size="10.5" font-weight="600" fill="#4C1D95">Structured Logging</text>
<text x="326" y="278" font-size="9.5" fill="#64748B">slog · per-endpoint log level control</text>
<rect x="312" y="291" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="291" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="303" font-size="10.5" font-weight="600" fill="#4C1D95">Panic Recovery</text>
<text x="326" y="317" font-size="9.5" fill="#64748B">Catches runtime panics, returns 500</text>
<rect x="312" y="330" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="330" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="342" font-size="10.5" font-weight="600" fill="#4C1D95">CORS</text>
<text x="326" y="356" font-size="9.5" fill="#64748B">Cross-origin request handling</text>
<rect x="312" y="369" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="369" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="381" font-size="10.5" font-weight="600" fill="#4C1D95">Rate Limiting</text>
<text x="326" y="395" font-size="9.5" fill="#64748B">Per-endpoint limits · ratelimit.LoadConfig</text>
<rect x="312" y="408" width="240" height="44" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="312" y="408" width="4" height="44" rx="2" fill="#4F46E5"/>
<text x="326" y="422" font-size="10.5" font-weight="700" fill="#312E81">JWT Authentication</text>
<text x="326" y="436" font-size="9.5" fill="#4338CA">Cognito token · Bearer / auth_token cookie</text>
<text x="326" y="448" font-size="9.5" fill="#64748B">lestrrat-go/jwx v2 · COGNITO_ENV_ID scope</text>
<rect x="312" y="457" width="240" height="44" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="312" y="457" width="4" height="44" rx="2" fill="#4F46E5"/>
<text x="326" y="471" font-size="10.5" font-weight="700" fill="#312E81">RBAC Authorization</text>
<text x="326" y="485" font-size="9.5" fill="#4338CA">Permit.io PDP · Fine-grained permissions</text>
<text x="326" y="497" font-size="9.5" fill="#64748B">permitio/permit-golang · super-admin bypass</text>
<rect x="312" y="506" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="506" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="518" font-size="10.5" font-weight="600" fill="#4C1D95">OpenAPI Validation</text>
<text x="326" y="532" font-size="9.5" fill="#64748B">Request validation against OAS3 schema</text>
<rect x="312" y="545" width="240" height="34" rx="6" fill="#FFFFFF" stroke="#EDE9FE" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="545" width="4" height="34" rx="2" fill="#7C3AED"/>
<text x="326" y="557" font-size="10.5" font-weight="600" fill="#4C1D95">Swagger UI</text>
<text x="326" y="571" font-size="9.5" fill="#64748B">/swagger/* · doc.json · doc.yaml</text>
<!-- Test user note -->
<rect x="312" y="584" width="240" height="34" rx="6" fill="#FEF3C7" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="312" y="584" width="4" height="34" rx="2" fill="#D97706"/>
<text x="326" y="596" font-size="10.5" font-weight="600" fill="#78350F">Test User Injection</text>
<text x="326" y="610" font-size="9.5" fill="#92400E">DISABLE_AUTH=true · X-Test-User-Subject</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ARROW 2 → 3 -->
<!-- ═══════════════════════════════════════════════════════════ -->
<line x1="562" y1="460" x2="571" y2="460" stroke="#94A3B8" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 3: CONTROLLERS -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="571" y="110" width="265" height="700" rx="10" fill="url(#col-bg-ctrl)" stroke="#FBCFE8" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="571" y="110" width="265" height="52" rx="10" fill="url(#grad-ctrl)"/>
<rect x="571" y="140" width="265" height="22" fill="url(#grad-ctrl)"/>
<text x="703.5" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">Controllers</text>
<text x="703.5" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#FBCFE8">api/queryAPI/ · Controllers struct</text>
<!-- Controllers outer struct box -->
<rect x="581" y="174" width="245" height="610" rx="8" fill="#FDF2F8" stroke="#F9A8D4" stroke-width="1.5"/>
<rect x="581" y="174" width="245" height="28" rx="8" fill="#DB2777"/>
<rect x="581" y="190" width="245" height="12" fill="#DB2777"/>
<text x="703.5" y="191" text-anchor="middle" font-size="10.5" font-weight="700" fill="#FFFFFF">Controllers { *Services }</text>
<!-- Controller items -->
<rect x="591" y="210" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="229" font-size="10.5" font-weight="500" fill="#831843">ClientController</text>
<text x="800" y="229" text-anchor="end" font-size="9.5" fill="#9D174D">client.go</text>
<rect x="591" y="243" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="262" font-size="10.5" font-weight="500" fill="#831843">DocumentController</text>
<text x="800" y="262" text-anchor="end" font-size="9.5" fill="#9D174D">documents.go</text>
<rect x="591" y="276" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="295" font-size="10.5" font-weight="500" fill="#831843">CollectorController</text>
<text x="800" y="295" text-anchor="end" font-size="9.5" fill="#9D174D">collector.go</text>
<rect x="591" y="309" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="328" font-size="10.5" font-weight="500" fill="#831843">FolderController</text>
<text x="800" y="328" text-anchor="end" font-size="9.5" fill="#9D174D">folders.go</text>
<rect x="591" y="342" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="361" font-size="10.5" font-weight="500" fill="#831843">LabelController</text>
<text x="800" y="361" text-anchor="end" font-size="9.5" fill="#9D174D">labels.go</text>
<rect x="591" y="375" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="394" font-size="10.5" font-weight="500" fill="#831843">FieldExtractionController</text>
<text x="800" y="394" text-anchor="end" font-size="9.5" fill="#9D174D">fieldextractions.go</text>
<rect x="591" y="408" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="427" font-size="10.5" font-weight="500" fill="#831843">CustomSchemaController</text>
<text x="800" y="427" text-anchor="end" font-size="9.5" fill="#9D174D">customschemas.go</text>
<rect x="591" y="441" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="460" font-size="10.5" font-weight="500" fill="#831843">CustomMetadataController</text>
<text x="800" y="460" text-anchor="end" font-size="9.5" fill="#9D174D">custommetadata.go</text>
<rect x="591" y="474" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="493" font-size="10.5" font-weight="500" fill="#831843">ExportController</text>
<text x="800" y="493" text-anchor="end" font-size="9.5" fill="#9D174D">export.go</text>
<rect x="591" y="507" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="526" font-size="10.5" font-weight="500" fill="#831843">EulaController</text>
<text x="800" y="526" text-anchor="end" font-size="9.5" fill="#9D174D">eulaHandlers.go</text>
<rect x="591" y="540" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="559" font-size="10.5" font-weight="500" fill="#831843">UISettingsController</text>
<text x="800" y="559" text-anchor="end" font-size="9.5" fill="#9D174D">uisettings.go</text>
<rect x="591" y="573" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="592" font-size="10.5" font-weight="500" fill="#831843">AdminController</text>
<text x="800" y="592" text-anchor="end" font-size="9.5" fill="#9D174D">adminHandlers.go</text>
<rect x="591" y="606" width="225" height="28" rx="5" fill="#FFFFFF" stroke="#FCE7F3" stroke-width="1" filter="url(#card-shadow)"/>
<text x="603" y="625" font-size="10.5" font-weight="500" fill="#831843">IdentityController</text>
<text x="800" y="625" text-anchor="end" font-size="9.5" fill="#9D174D">identityHandlers.go</text>
<!-- Background task note -->
<rect x="581" y="650" width="245" height="44" rx="6" fill="#FDF4FF" stroke="#E879F9" stroke-width="1"/>
<text x="703.5" y="668" text-anchor="middle" font-size="10" font-weight="600" fill="#701A75">Background Task Runner</text>
<text x="703.5" y="683" text-anchor="middle" font-size="9.5" fill="#86198F">internal/backgroundtask/ · alitto/pond v2</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ARROW 3 → 4 -->
<!-- ═══════════════════════════════════════════════════════════ -->
<line x1="836" y1="460" x2="845" y2="460" stroke="#94A3B8" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 4: SERVICE LAYER -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="845" y="110" width="295" height="700" rx="10" fill="url(#col-bg-svc)" stroke="#A7F3D0" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="845" y="110" width="295" height="52" rx="10" fill="url(#grad-svc)"/>
<rect x="845" y="140" width="295" height="22" fill="url(#grad-svc)"/>
<text x="992.5" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">Service Layer</text>
<text x="992.5" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#A7F3D0">internal/*/service.go · Domain business logic</text>
<!-- Service items -->
<rect x="855" y="174" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="174" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="190" font-size="11" font-weight="600" fill="#064E3B">Client Service</text>
<text x="869" y="207" font-size="9.5" fill="#64748B">internal/client/ · CRUD, normalize, sync state</text>
<rect x="855" y="222" width="275" height="70" rx="6" fill="#F0FDF4" stroke="#BBF7D0" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="855" y="222" width="4" height="70" rx="2" fill="#10B981"/>
<text x="869" y="238" font-size="11" font-weight="700" fill="#064E3B">Document Service</text>
<text x="869" y="252" font-size="9.5" fill="#064E3B">internal/document/ · Lifecycle management</text>
<text x="869" y="265" font-size="9.5" fill="#6B7280"> upload/ · download/ · batch/ · init/</text>
<text x="869" y="278" font-size="9.5" fill="#6B7280"> sync/ · store/ · clean/ · delete.go</text>
<rect x="855" y="297" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="297" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="313" font-size="11" font-weight="600" fill="#064E3B">Collector Service</text>
<text x="869" y="330" font-size="9.5" fill="#64748B">internal/collector/ · internal/collector/set/</text>
<rect x="855" y="345" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="345" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="361" font-size="11" font-weight="600" fill="#064E3B">Folder &amp; Label Services</text>
<text x="869" y="378" font-size="9.5" fill="#64748B">internal/folder/ · internal/label/</text>
<rect x="855" y="393" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="393" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="409" font-size="11" font-weight="600" fill="#064E3B">Field Extraction Service</text>
<text x="869" y="426" font-size="9.5" fill="#64748B">internal/fieldextraction/ · Versioned history</text>
<rect x="855" y="441" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="441" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="457" font-size="11" font-weight="600" fill="#064E3B">Custom Schema Service</text>
<text x="869" y="474" font-size="9.5" fill="#64748B">internal/customschema/ · Schema validation</text>
<rect x="855" y="489" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="489" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="505" font-size="11" font-weight="600" fill="#064E3B">Export Service</text>
<text x="869" y="522" font-size="9.5" fill="#64748B">internal/export/ · Result output management</text>
<rect x="855" y="537" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="537" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="553" font-size="11" font-weight="600" fill="#064E3B">EULA Service</text>
<text x="869" y="570" font-size="9.5" fill="#64748B">internal/eula/ · Versioning &amp; compliance</text>
<rect x="855" y="585" width="275" height="43" rx="6" fill="#FFFFFF" stroke="#D1FAE5" stroke-width="1" filter="url(#card-shadow)"/>
<rect x="855" y="585" width="4" height="43" rx="2" fill="#059669"/>
<text x="869" y="601" font-size="11" font-weight="600" fill="#064E3B">UI Settings &amp; User Management</text>
<text x="869" y="618" font-size="9.5" fill="#64748B">internal/uisettings/ · internal/usermanagement/</text>
<!-- SQS publish note -->
<rect x="855" y="640" width="275" height="44" rx="6" fill="#FFF7ED" stroke="#FED7AA" stroke-width="1.5"/>
<rect x="855" y="640" width="4" height="44" rx="2" fill="#EA580C"/>
<text x="869" y="657" font-size="10.5" font-weight="700" fill="#7C2D12">Publishes to SQS Queues</text>
<text x="869" y="671" font-size="9.5" fill="#9A3412">Triggers async pipeline stages</text>
<text x="869" y="683" font-size="9.5" fill="#9A3412">store_event · document_init · client_sync</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ARROW 4 → 5 -->
<!-- ═══════════════════════════════════════════════════════════ -->
<line x1="1140" y1="460" x2="1149" y2="460" stroke="#94A3B8" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 5: REPOSITORY LAYER -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="1149" y="110" width="265" height="700" rx="10" fill="url(#col-bg-repo)" stroke="#FDE68A" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="1149" y="110" width="265" height="52" rx="10" fill="url(#grad-repo)"/>
<rect x="1149" y="140" width="265" height="22" fill="url(#grad-repo)"/>
<text x="1281.5" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">Repository Layer</text>
<text x="1281.5" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#FDE68A">internal/database/repository/ · sqlc v1.27</text>
<!-- Queries struct label -->
<rect x="1159" y="174" width="245" height="36" rx="6" fill="#D97706" filter="url(#card-shadow)"/>
<text x="1281.5" y="189" text-anchor="middle" font-size="11" font-weight="700" fill="#FFFFFF">*Queries struct</text>
<text x="1281.5" y="203" text-anchor="middle" font-size="9.5" fill="#FEF3C7">Type-safe SQL · No ORM · pgx/v5</text>
<!-- Generated files -->
<rect x="1159" y="218" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="232" font-size="9.5" font-weight="600" fill="#78350F">client.sql.go</text>
<text x="1390" y="232" text-anchor="end" font-size="9" fill="#92400E">Client CRUD queries</text>
<text x="1171" y="244" font-size="9" fill="#9CA3AF">GetClient · ListClients · CreateClient · UpdateClient</text>
<rect x="1159" y="252" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="266" font-size="9.5" font-weight="600" fill="#78350F">document.sql.go</text>
<text x="1390" y="266" text-anchor="end" font-size="9" fill="#92400E">Document queries</text>
<text x="1171" y="278" font-size="9" fill="#9CA3AF">GetDocument · ListDocuments · SetState</text>
<rect x="1159" y="286" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="300" font-size="9.5" font-weight="600" fill="#78350F">batch.sql.go</text>
<text x="1390" y="300" text-anchor="end" font-size="9" fill="#92400E">Batch queries</text>
<text x="1171" y="312" font-size="9" fill="#9CA3AF">CreateBatch · GetBatch · UpdateBatchItem</text>
<rect x="1159" y="320" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="334" font-size="9.5" font-weight="600" fill="#78350F">folder.sql.go · label.sql.go</text>
<text x="1390" y="334" text-anchor="end" font-size="9" fill="#92400E">Org queries</text>
<text x="1171" y="346" font-size="9" fill="#9CA3AF">Folder hierarchy · label assignment</text>
<rect x="1159" y="354" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="368" font-size="9.5" font-weight="600" fill="#78350F">fieldextractions.sql.go</text>
<text x="1390" y="368" text-anchor="end" font-size="9" fill="#92400E">Extraction queries</text>
<text x="1171" y="380" font-size="9" fill="#9CA3AF">Versioned field extraction records</text>
<rect x="1159" y="388" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="402" font-size="9.5" font-weight="600" fill="#78350F">customschemas.sql.go</text>
<text x="1390" y="402" text-anchor="end" font-size="9" fill="#92400E">Schema queries</text>
<text x="1171" y="414" font-size="9" fill="#9CA3AF">CreateSchema · GetSchema · ListVersions</text>
<rect x="1159" y="422" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="436" font-size="9.5" font-weight="600" fill="#78350F">custommetadata.sql.go</text>
<text x="1390" y="436" text-anchor="end" font-size="9" fill="#92400E">Metadata queries</text>
<text x="1171" y="448" font-size="9" fill="#9CA3AF">UpsertMetadata · GetMetadata · DeleteMetadata</text>
<rect x="1159" y="456" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="470" font-size="9.5" font-weight="600" fill="#78350F">eula.sql.go · uisettings.sql.go</text>
<text x="1390" y="470" text-anchor="end" font-size="9" fill="#92400E">Settings queries</text>
<text x="1171" y="482" font-size="9" fill="#9CA3AF">EULA versioning · UI preference storage</text>
<rect x="1159" y="490" width="245" height="30" rx="5" fill="#FFFFFF" stroke="#FDE68A" stroke-width="1" filter="url(#card-shadow)"/>
<text x="1171" y="504" font-size="9.5" font-weight="600" fill="#78350F">models.go</text>
<text x="1390" y="504" text-anchor="end" font-size="9" fill="#92400E">Domain types</text>
<text x="1171" y="516" font-size="9" fill="#9CA3AF">All generated entity structs &amp; enums</text>
<!-- Transaction support -->
<rect x="1159" y="530" width="245" height="34" rx="6" fill="#FEF3C7" stroke="#FDE68A" stroke-width="1.5" filter="url(#card-shadow)"/>
<text x="1281.5" y="544" text-anchor="middle" font-size="10" font-weight="700" fill="#78350F">Transaction Support</text>
<text x="1281.5" y="558" text-anchor="middle" font-size="9.5" fill="#92400E">Queries.WithTx(tx) · pgx.Tx wrapping</text>
<!-- Config provider note -->
<rect x="1159" y="572" width="245" height="50" rx="6" fill="#FFFBEB" stroke="#FDE68A" stroke-width="1"/>
<text x="1281.5" y="588" text-anchor="middle" font-size="10" font-weight="600" fill="#92400E">Database Config Provider</text>
<text x="1281.5" y="602" text-anchor="middle" font-size="9.5" fill="#9CA3AF">database.ConfigProvider interface</text>
<text x="1281.5" y="614" text-anchor="middle" font-size="9.5" fill="#9CA3AF">GetDBQueries() · Connection pool</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- ARROW 5 → 6 -->
<!-- ═══════════════════════════════════════════════════════════ -->
<line x1="1414" y1="460" x2="1423" y2="460" stroke="#94A3B8" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- COLUMN 6: DATABASE -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="1423" y="110" width="360" height="700" rx="10" fill="url(#col-bg-db)" stroke="#FECACA" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="1423" y="110" width="360" height="52" rx="10" fill="url(#grad-db)"/>
<rect x="1423" y="140" width="360" height="22" fill="url(#grad-db)"/>
<text x="1603" y="131" text-anchor="middle" font-size="13.5" font-weight="700" fill="#FFFFFF" letter-spacing="0.3">Data &amp; Storage Layer</text>
<text x="1603" y="150" text-anchor="middle" font-size="10" font-weight="400" fill="#FECACA">PostgreSQL 17.2 · AWS S3 · AWS SQS</text>
<!-- PostgreSQL section -->
<rect x="1433" y="174" width="340" height="240" rx="8" fill="#FFFFFF" stroke="#FECACA" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="1433" y="174" width="340" height="32" rx="8" fill="#DC2626"/>
<rect x="1433" y="194" width="340" height="12" fill="#DC2626"/>
<text x="1603" y="192" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">PostgreSQL 17.2</text>
<text x="1445" y="223" font-size="10" font-weight="600" fill="#7F1D1D">Schema</text>
<text x="1445" y="237" font-size="9.5" fill="#374151">126+ golang-migrate migrations</text>
<text x="1445" y="250" font-size="9.5" fill="#374151">UUID v7 primary keys throughout</text>
<text x="1445" y="263" font-size="9.5" fill="#374151">Custom enums (document states, batch outcomes)</text>
<text x="1445" y="276" font-size="9.5" fill="#374151">Views: fullClients · joined denormalized data</text>
<line x1="1445" y1="284" x2="1763" y2="284" stroke="#FCA5A5" stroke-width="0.8"/>
<text x="1445" y="299" font-size="10" font-weight="600" fill="#7F1D1D">Connection</text>
<text x="1445" y="313" font-size="9.5" fill="#374151">Driver: pgx/v5 · Pooled via puddle</text>
<text x="1445" y="326" font-size="9.5" fill="#374151">internal/serviceconfig/database/</text>
<text x="1445" y="339" font-size="9.5" fill="#374151">Mixed naming: camelCase (legacy) + snake_case</text>
<text x="1445" y="352" font-size="9.5" fill="#6B7280">No caching layer — all reads direct to PG</text>
<text x="1445" y="365" font-size="9.5" fill="#6B7280">Testcontainers for real DB in integration tests</text>
<!-- AWS S3 section -->
<rect x="1433" y="424" width="340" height="120" rx="8" fill="#FFFFFF" stroke="#FECACA" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="1433" y="424" width="340" height="32" rx="8" fill="#DC2626" opacity="0.85"/>
<rect x="1433" y="444" width="340" height="12" fill="#DC2626" opacity="0.85"/>
<text x="1603" y="442" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">AWS S3 · Document Storage</text>
<text x="1445" y="472" font-size="9.5" fill="#374151">BUCKET (main) · BUCKET_IN (inbound uploads)</text>
<text x="1445" y="485" font-size="9.5" fill="#374151">internal/serviceconfig/objectstore/</text>
<text x="1445" y="498" font-size="9.5" fill="#374151">AWS SDK v2 · LocalStack for local dev</text>
<text x="1445" y="511" font-size="9.5" fill="#374151">Streams original files (no format conversion)</text>
<text x="1445" y="524" font-size="9.5" fill="#6B7280">AWS_ENDPOINT_URL_S3 override for local</text>
<!-- AWS SQS section -->
<rect x="1433" y="554" width="340" height="160" rx="8" fill="#FFFFFF" stroke="#FECACA" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="1433" y="554" width="340" height="32" rx="8" fill="#DC2626" opacity="0.7"/>
<rect x="1433" y="574" width="340" height="12" fill="#DC2626" opacity="0.7"/>
<text x="1603" y="572" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">AWS SQS · Async Pipeline Queues</text>
<text x="1445" y="600" font-size="9.5" fill="#374151">store_event → storeEventRunner</text>
<text x="1445" y="614" font-size="9.5" fill="#374151">document_init → docInitRunner</text>
<text x="1445" y="628" font-size="9.5" fill="#374151">document_sync → docSyncRunner</text>
<text x="1445" y="642" font-size="9.5" fill="#374151">document_clean → docCleanRunner</text>
<text x="1445" y="656" font-size="9.5" fill="#374151">document_text → docTextRunner</text>
<text x="1445" y="670" font-size="9.5" fill="#374151">client_sync · query_runner · query_sync</text>
<text x="1445" y="684" font-size="9.5" fill="#6B7280">LocalStack simulation · AWS_ENDPOINT_URL_SQS</text>
<text x="1445" y="698" font-size="9.5" fill="#6B7280">9 queues · runner.Server[B] generic polling loop</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- EXTERNAL SERVICES SECTION -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="8" y="828" width="1775" height="270" rx="10" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1.5" filter="url(#col-shadow)"/>
<rect x="8" y="828" width="1775" height="38" rx="10" fill="#0F172A"/>
<rect x="8" y="854" width="1775" height="12" fill="#0F172A"/>
<text x="895.5" y="852" text-anchor="middle" font-size="13" font-weight="700" fill="#FFFFFF" letter-spacing="1">EXTERNAL SERVICES &amp; INFRASTRUCTURE</text>
<!-- Cognito box -->
<rect x="30" y="876" width="400" height="200" rx="8" fill="#FFFFFF" stroke="#BFDBFE" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="30" y="876" width="400" height="32" rx="8" fill="#2563EB"/>
<rect x="30" y="896" width="400" height="12" fill="#2563EB"/>
<text x="230" y="894" text-anchor="middle" font-size="12" font-weight="700" fill="#FFFFFF">AWS Cognito</text>
<text x="48" y="924" font-size="10.5" font-weight="600" fill="#1E3A8A">JWT Authentication</text>
<text x="48" y="939" font-size="10" fill="#374151">User pool · us-east-2 region</text>
<text x="48" y="953" font-size="10" fill="#374151">Token: Authorization header / auth_token cookie</text>
<text x="48" y="967" font-size="10" fill="#374151">COGNITO_ENVIRONMENT_ID — environment-scoped access</text>
<text x="48" y="981" font-size="10" fill="#374151">Login/callback/logout OAuth2 flows</text>
<text x="48" y="995" font-size="10" fill="#6B7280">internal/cognitoauth/ · lestrrat-go/jwx v2</text>
<text x="48" y="1009" font-size="10" fill="#6B7280">Connects to: Middleware JWT Auth layer</text>
<!-- Permit.io box -->
<rect x="448" y="876" width="400" height="200" rx="8" fill="#FFFFFF" stroke="#DDD6FE" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="448" y="876" width="400" height="32" rx="8" fill="#7C3AED"/>
<rect x="448" y="896" width="400" height="12" fill="#7C3AED"/>
<text x="648" y="894" text-anchor="middle" font-size="12" font-weight="700" fill="#FFFFFF">Permit.io PDP</text>
<text x="466" y="924" font-size="10.5" font-weight="600" fill="#4C1D95">RBAC Authorization</text>
<text x="466" y="939" font-size="10" fill="#374151">Policy Decision Point — runs locally port 7000</text>
<text x="466" y="953" font-size="10" fill="#374151">Fine-grained permissions after JWT validation</text>
<text x="466" y="967" font-size="10" fill="#374151">Super-admin role bypasses env access control</text>
<text x="466" y="981" font-size="10" fill="#374151">permitio/permit-golang v1.2.5 SDK</text>
<text x="466" y="995" font-size="10" fill="#6B7280">internal/cognitoauth/permitio.go</text>
<text x="466" y="1009" font-size="10" fill="#6B7280">Connects to: Middleware RBAC Authorization layer</text>
<!-- Prometheus box -->
<rect x="866" y="876" width="380" height="200" rx="8" fill="#FFFFFF" stroke="#FED7AA" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="866" y="876" width="380" height="32" rx="8" fill="#EA580C"/>
<rect x="866" y="896" width="380" height="12" fill="#EA580C"/>
<text x="1056" y="894" text-anchor="middle" font-size="12" font-weight="700" fill="#FFFFFF">Prometheus + LocalStack</text>
<text x="884" y="924" font-size="10.5" font-weight="600" fill="#7C2D12">Observability &amp; Local AWS Simulation</text>
<text x="884" y="939" font-size="10" fill="#374151">Prometheus metrics on /metrics · port 9090 (local)</text>
<text x="884" y="953" font-size="10" fill="#374151">LocalStack simulates S3 and SQS for local dev</text>
<text x="884" y="967" font-size="10" fill="#374151">Testcontainers: real PostgreSQL in integration tests</text>
<text x="884" y="981" font-size="10" fill="#374151">observability.ConfigProvider interface</text>
<text x="884" y="995" font-size="10" fill="#6B7280">task compose:refresh — spins up full local stack</text>
<text x="884" y="1009" font-size="10" fill="#6B7280">Connects to: Middleware Prometheus layer</text>
<!-- Async Pipeline box -->
<rect x="1264" y="876" width="506" height="200" rx="8" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="1.5" filter="url(#card-shadow)"/>
<rect x="1264" y="876" width="506" height="32" rx="8" fill="#059669"/>
<rect x="1264" y="896" width="506" height="12" fill="#059669"/>
<text x="1517" y="894" text-anchor="middle" font-size="12" font-weight="700" fill="#FFFFFF">Async Document Processing Pipeline</text>
<text x="1282" y="924" font-size="10.5" font-weight="600" fill="#064E3B">SQS Runner Services · cmd/*Runner/ · port range 80818090</text>
<text x="1282" y="941" font-size="10" fill="#374151">S3 Upload Event</text>
<text x="1350" y="941" font-size="10" fill="#94A3B8"></text>
<text x="1365" y="941" font-size="10" fill="#374151">storeEventRunner</text>
<text x="1470" y="941" font-size="10" fill="#94A3B8"></text>
<text x="1485" y="941" font-size="10" fill="#374151">docInitRunner</text>
<text x="1574" y="941" font-size="10" fill="#94A3B8"></text>
<text x="1589" y="941" font-size="10" fill="#374151">docSyncRunner</text>
<text x="1282" y="957" font-size="10" fill="#94A3B8"></text>
<text x="1297" y="957" font-size="10" fill="#374151">docCleanRunner</text>
<text x="1392" y="957" font-size="10" fill="#94A3B8"></text>
<text x="1407" y="957" font-size="10" fill="#374151">docTextRunner</text>
<text x="1492" y="957" font-size="10" fill="#94A3B8"></text>
<text x="1507" y="957" font-size="10" fill="#374151">PostgreSQL update</text>
<text x="1282" y="974" font-size="10" fill="#374151">Each runner: runner.Controller[B] generic interface</text>
<text x="1282" y="989" font-size="10" fill="#374151">Process(ctx, body B) bool → true=ACK, false=requeue</text>
<text x="1282" y="1004" font-size="10" fill="#6B7280">Also: clientSyncRunner · queryRunner · querySyncRunner · queryVersionSyncRunner</text>
<text x="1282" y="1019" font-size="10" fill="#6B7280">api/{runner}/runner.go · Polls SQS every ~1 second</text>
<!-- ═══════════════════════════════════════════════════════════ -->
<!-- FOOTER -->
<!-- ═══════════════════════════════════════════════════════════ -->
<rect x="0" y="1094" width="1800" height="26" fill="#0F172A"/>
<text x="900" y="1110" text-anchor="middle" font-size="10" fill="#475569">Query Orchestration API · feature/mutable-metadata1 · Go 1.23 · Echo v4 · sqlc v1.27 · PostgreSQL 17.2 · AWS SDK v2</text>
</svg>