Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/a17-f002-stomp.svg
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

177 lines
32 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="3120" viewBox="0 0 1200 3120" role="img">
<title>terminal evidence</title>
<desc>Terminal-style rendering generated from retained command output. Sensitive-looking values are redacted in the visual asset.</desc>
<rect x="1" y="1" width="1198" height="3118" rx="14" fill="#0d1117" stroke="#30363d"/>
<rect x="1" y="1" width="1198" height="44" rx="14" fill="#161b22"/>
<rect x="1" y="30" width="1198" height="14" fill="#161b22"/>
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
<circle cx="64" cy="22" r="6" fill="#28c840"/>
<text x="92" y="27" fill="#8b949e" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">terminal evidence</text>
<text x="24" y="68" fill="#c9d1d9" font-size="15" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">$ set -e
set -o pipefail
W=src/adapter/inbound/websocket/src/main/java/dev/caskeleton/adapter/inbound/websocket
strip() </tspan><tspan x="24" dy="22">{ sed -E &#x27;s#^src/[^:]*/websocket/([^:]*/)?([^:/]+):([0-9]+):# \2:\3 #&#x27;; }
cut_range() { awk -v f=&quot;$2&quot; -v a=&quot;$3&quot; -v b=&quot;$</tspan><tspan x="24" dy="22">4&quot; &#x27;NR&gt;=a &amp;&amp; NR&lt;=b {printf &quot; %s:%d %s\n&quot;, f, NR, $0}&#x27; &quot;$1&quot;; }
echo &quot;# ca-skeleton.websocket 어댑터가 등록하는 인터셉터</tspan><tspan x="24" dy="22">&quot;
for f in AuthenticatedHandshakeInterceptor WebSocketInboundAuthorizationInterceptor; do
printf &quot; %-44s %s 줄\n&quot; </tspan><tspan x="24" dy="22">&quot;$f.java&quot; &quot;$(wc -l &lt; $W/stomp/$f.java)&quot;
done
awk &#x27;NR&gt;=30 &amp;&amp; NR&lt;=60 &amp;&amp; (/Interceptor/ || /addInterceptors/ || /configureC</tspan><tspan x="24" dy="22">lientInboundChannel/ || /ConditionalOnProperty/) {printf &quot; stomp/WebSocketConfig.java:%d %s\n&quot;, NR, $0}&#x27; $W/stomp/WebSo</tspan><tspan x="24" dy="22">cketConfig.java
echo
echo &quot;# 같은 채널에 붙는 세 번째 인터셉터와 그 게이트&quot;
printf &quot; %-44s %s 줄\n&quot; &quot;advanced/stomp/St</tspan><tspan x="24" dy="22">ompSecurityInterceptor.java&quot; &quot;$(wc -l &lt; $W/advanced/stomp/StompSecurityInterceptor.java)&quot;
cut_range $W/advanced/stomp/St</tspan><tspan x="24" dy="22">ompConfiguration.java advanced/stomp/StompConfiguration.java 24 27
awk &#x27;NR&gt;=33 &amp;&amp; NR&lt;=41 &amp;&amp; (/securityInterceptor/ || /c</tspan><tspan x="24" dy="22">onfigureClientInboundChannel/) {printf &quot; advanced/stomp/StompConfiguration.java:%d %s\n&quot;, NR, $0}&#x27; $W/advanced/stomp/St</tspan><tspan x="24" dy="22">ompConfiguration.java
echo
echo &quot;# 이 모듈이 출하 런타임 그래프에 있는가&quot;
awk &#x27;NR&gt;=196 &amp;&amp; NR&lt;=200 {printf &quot; app-bootst</tspan><tspan x="24" dy="22">rap/build.gradle:%d %s\n&quot;, NR, $0}&#x27; src/app-bootstrap/build.gradle
cut_range src/adapter/inbound/websocket/CLAUDE.md web</tspan><tspan x="24" dy="22">socket/CLAUDE.md 21 23
echo
echo &quot;# 정책 계층 — 네 패키지의 파일과 선언 형태와 줄 수&quot;
for d in security authz idempote</tspan><tspan x="24" dy="22">ncy budget; do
for f in $(find $W/$d -name &#x27;*.java&#x27; | sort); do
k=$(grep -oE &#x27;public (final class|record|enum|inte</tspan><tspan x="24" dy="22">rface|class)&#x27; $f | head -1 || true)
printf &quot; %-12s %-38s %-16s %s 줄\n&quot; &quot;$d&quot; &quot;$(basename $f)&quot; &quot;${k#public }&quot; &quot;$(wc </tspan><tspan x="24" dy="22">-l &lt; $f)&quot;
done
done
printf &quot; 합계 %s 파일\n&quot; &quot;$(find $W/security $W/authz $W/idempotency $W/budget -name &#x27;*.java&#x27; | w</tspan><tspan x="24" dy="22">c -l)&quot;
echo
echo &quot;# 그 열 타입을 stomp 패키지가 참조하는가, 그리고 어디서 생성되는가&quot;
for f in $(find $W/security $W/au</tspan><tspan x="24" dy="22">thz $W/idempotency $W/budget -name &#x27;*.java&#x27; | sed &#x27;s|.*/||;s|\.java$||&#x27; | sort); do
s=$(grep -rl &quot;\b$f\b&quot; $W/stomp 2&gt;/</tspan><tspan x="24" dy="22">dev/null | wc -l || true)
n=$(git grep -c &quot;new $f(&quot; -- &#x27;src/**/*.java&#x27; 2&gt;/dev/null | grep &#x27;/main/&#x27; | wc -l || true)
</tspan><tspan x="24" dy="22">b=$(git grep -l &quot;$f &quot; -- &#x27;src/**/*.java&#x27; 2&gt;/dev/null | grep &#x27;/main/&#x27; | xargs grep -l &#x27;@Bean&#x27; 2&gt;/dev/null | wc -l || true</tspan><tspan x="24" dy="22">)
printf &quot; %-34s stomp참조=%s main생성=%s 같은파일에@Bean있는main=%s\n&quot; &quot;$f&quot; &quot;$s&quot; &quot;$n&quot; &quot;$b&quot;
done
echo
echo &quot;# 원문</tspan><tspan x="24" dy="22">이 22 로 적은 수의 출처&quot;
grep -n &#x27;^# Sub-scope 04&#x27; /shared/document-detail/clean-architecture-backend-template/analysis/</tspan><tspan x="24" dy="22">17-adapter-inbound-websocket.md \
| cut -c1-150 | sed &#x27;s#^# analysis/17:#&#x27;
t=0; for d in security authz idempotency b</tspan><tspan x="24" dy="22">udget error observability admin release; do
n=$(find $W/$d -name &#x27;*.java&#x27; | wc -l); t=$((t+n)); printf &quot; %-14s %s\n&quot; </tspan><tspan x="24" dy="22">&quot;$d&quot; &quot;$n&quot;
done
echo &quot; 여덟 패키지 합계 $t&quot;
echo
echo &quot;# MessageAuthorizationPolicy 를 받는 자리와 그 받는 쪽의 생성자&quot;</tspan><tspan x="24" dy="22">
grep -n &#x27;public &#x27; $W/authz/MessageAuthorizationPolicy.java | sed -E &#x27;s#^([0-9]+): *# authz/MessageAuthorizationPolicy.</tspan><tspan x="24" dy="22">java:\1 #&#x27;
git grep -n &#x27;MessageAuthorizationPolicy&#x27; -- &#x27;src/**/*.java&#x27; | grep &#x27;/main/&#x27; | grep -v &#x27;/MessageAuthorization</tspan><tspan x="24" dy="22">Policy.java&#x27; | strip
grep -n &#x27;public WebSocketPlatformStartupValidator\|public void validate\|undeclaredAmong&#x27; $W/config</tspan><tspan x="24" dy="22">/WebSocketPlatformStartupValidator.java \
| sed -E &#x27;s#^([0-9]+): *# config/WebSocketPlatformStartupValidator.java:\1 </tspan><tspan x="24" dy="22"> #&#x27;
git grep -n &#x27;new WebSocketPlatformStartupValidator(&#x27; -- &#x27;src/**/*.java&#x27; | strip
echo
echo &quot;# 리프의 CLAUDE.md 가 적</tspan><tspan x="24" dy="22">는 범위와 인바운드 정책 절 전체&quot;
cut_range src/adapter/inbound/websocket/CLAUDE.md websocket/CLAUDE.md 15 18
cut_range s</tspan><tspan x="24" dy="22">rc/adapter/inbound/websocket/CLAUDE.md websocket/CLAUDE.md 52 60
</tspan></text>
<text x="24" y="772" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">cwd: /shared/codebase/clean-architecture-backend-template</tspan></text>
<text x="24" y="794" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">time: 2026-09-04T00:20:53+00:00 · exit 0</tspan></text>
<line x1="24" y1="808" x2="1176" y2="808" stroke="#30363d"/>
<text x="24" y="848" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># ca-skeleton.websocket 어댑터가 등록하는 인터셉터 둘</tspan></text>
<text x="24" y="870" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AuthenticatedHandshakeInterceptor.java 34 줄</tspan></text>
<text x="24" y="892" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketInboundAuthorizationInterceptor.java 53 줄</tspan></text>
<text x="24" y="914" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:32 @ConditionalOnProperty(prefix = &quot;ca-skeleton.websocket&quot;, name = &quot;enabled&quot;, havingValue = &quot;true&quot;)</tspan></text>
<text x="24" y="936" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:36 private final AuthenticatedHandshakeInterceptor handshakeInterceptor =</tspan></text>
<text x="24" y="958" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:37 new AuthenticatedHandshakeInterceptor();</tspan></text>
<text x="24" y="980" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:40 private final WebSocketInboundAuthorizationInterceptor inboundAuthorization;</tspan></text>
<text x="24" y="1002" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:44 this.inboundAuthorization = new WebSocketInboundAuthorizationInterceptor(properties);</tspan></text>
<text x="24" y="1024" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:53 .addInterceptors(handshakeInterceptor)</tspan></text>
<text x="24" y="1046" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> stomp/WebSocketConfig.java:58 public void configureClientInboundChannel(ChannelRegistration registration) {</tspan></text>
<text x="24" y="1068" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1090" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 같은 채널에 붙는 세 번째 인터셉터와 그 게이트</tspan></text>
<text x="24" y="1112" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompSecurityInterceptor.java 96 줄</tspan></text>
<text x="24" y="1134" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:24 @ConditionalOnProperty(</tspan></text>
<text x="24" y="1156" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:25 prefix = &quot;app.websocket-platform.advanced.stomp&quot;,</tspan></text>
<text x="24" y="1178" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:26 name = &quot;enabled&quot;,</tspan></text>
<text x="24" y="1200" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:27 havingValue = &quot;true&quot;)</tspan></text>
<text x="24" y="1222" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:33 public StompConfiguration(StompProfile profile, StompSecurityInterceptor securityI</tspan><tspan x="24" dy="22">nterceptor) {</tspan></text>
<text x="24" y="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:35 this.securityInterceptor = securityInterceptor;</tspan></text>
<text x="24" y="1288" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:39 public void configureClientInboundChannel(ChannelRegistration registration) {</tspan></text>
<text x="24" y="1310" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> advanced/stomp/StompConfiguration.java:40 registration.interceptors(securityInterceptor);</tspan></text>
<text x="24" y="1332" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1354" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 이 모듈이 출하 런타임 그래프에 있는가</tspan></text>
<text x="24" y="1376" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> app-bootstrap/build.gradle:196 // Explicit qualification-only composition. These projects remain absent from main</tspan></text>
<text x="24" y="1398" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> app-bootstrap/build.gradle:197 // api/implementation/compileOnly/runtimeOnly and therefore from both shipped runtime graph</tspan><tspan x="24" dy="22">s.</tspan></text>
<text x="24" y="1442" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> app-bootstrap/build.gradle:198 conditionalTransportTestImplementation project(&#x27;:adapter:inbound:graphql&#x27;)</tspan></text>
<text x="24" y="1464" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> app-bootstrap/build.gradle:199 conditionalTransportTestImplementation project(&#x27;:adapter:inbound:grpc&#x27;)</tspan></text>
<text x="24" y="1486" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> app-bootstrap/build.gradle:200 conditionalTransportTestImplementation project(&#x27;:adapter:inbound:websocket&#x27;)</tspan></text>
<text x="24" y="1508" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:21 The module is absent from both shipped runtime compositions. Being registered and tested does not</tspan></text>
<text x="24" y="1530" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:22 activate it. A future composition must deliberately add the registered dependency and set</tspan></text>
<text x="24" y="1552" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:23 `ca-skeleton.websocket.enabled=true` with explicit safe origins.</tspan></text>
<text x="24" y="1574" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1596" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 정책 계층 — 네 패키지의 파일과 선언 형태와 줄 수</tspan></text>
<text x="24" y="1618" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> security WebSocketAuthenticationProfile.java enum 57 줄</tspan></text>
<text x="24" y="1640" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> security WebSocketConnectionTicket.java record 77 줄</tspan></text>
<text x="24" y="1662" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> security WebSocketOriginPolicy.java final class 116 줄</tspan></text>
<text x="24" y="1684" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> security WebSocketTicketStore.java interface 32 줄</tspan></text>
<text x="24" y="1706" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz MessageAuthorizationPolicy.java final class 93 줄</tspan></text>
<text x="24" y="1728" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency CommandReconciliation.java interface 84 줄</tspan></text>
<text x="24" y="1750" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency CommittedResultLedger.java interface 61 줄</tspan></text>
<text x="24" y="1772" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency WebSocketCommandKey.java record 40 줄</tspan></text>
<text x="24" y="1794" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency WebSocketCommandOutcome.java enum 33 줄</tspan></text>
<text x="24" y="1816" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> budget WebSocketConnectionBudget.java record 103 줄</tspan></text>
<text x="24" y="1838" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 합계 10 파일</tspan></text>
<text x="24" y="1860" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1882" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 열 타입을 stomp 패키지가 참조하는가, 그리고 어디서 생성되는가</tspan></text>
<text x="24" y="1904" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> CommandReconciliation stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="1926" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> CommittedResultLedger stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="1948" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> MessageAuthorizationPolicy stomp참조=0 main생성=1 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="1970" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketAuthenticationProfile stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="1992" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketCommandKey stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="2014" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketCommandOutcome stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="2036" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketConnectionBudget stomp참조=0 main생성=1 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="2058" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketConnectionTicket stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="2080" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketOriginPolicy stomp참조=0 main생성=1 같은파일에@Bean있는main=1</tspan></text>
<text x="24" y="2102" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketTicketStore stomp참조=0 main생성=0 같은파일에@Bean있는main=0</tspan></text>
<text x="24" y="2124" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="2146" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 원문이 22 로 적은 수의 출처</tspan></text>
<text x="24" y="2168" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> analysis/17:235:# Sub-scope 04 — `security` + `authz` + `idempotency` + `budget` + `error` + `observability` + `admin` + `rel</tspan><tspan x="24" dy="22">ease` (31 files, main 22 + test 9)</tspan></text>
<text x="24" y="2212" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> security 4</tspan></text>
<text x="24" y="2234" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz 1</tspan></text>
<text x="24" y="2256" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency 4</tspan></text>
<text x="24" y="2278" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> budget 1</tspan></text>
<text x="24" y="2300" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> error 5</tspan></text>
<text x="24" y="2322" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> observability 2</tspan></text>
<text x="24" y="2344" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> admin 2</tspan></text>
<text x="24" y="2366" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> release 3</tspan></text>
<text x="24" y="2388" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 여덟 패키지 합계 22</tspan></text>
<text x="24" y="2410" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="2432" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># MessageAuthorizationPolicy 를 받는 자리와 그 받는 쪽의 생성자</tspan></text>
<text x="24" y="2454" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:26 public final class MessageAuthorizationPolicy {</tspan></text>
<text x="24" y="2476" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:39 public static MessageAuthorizationPolicy of(Map&lt;WebSocketMessageType, Set&lt;String&gt;&gt; r</tspan><tspan x="24" dy="22">equirements) {</tspan></text>
<text x="24" y="2520" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:57 public boolean permits(WebSocketMessageType type, Set&lt;String&gt; heldAuthorities) {</tspan></text>
<text x="24" y="2542" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:70 public boolean declares(WebSocketMessageType type) {</tspan></text>
<text x="24" y="2564" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:81 public java.util.List&lt;WebSocketMessageType&gt; undeclaredAmong(</tspan></text>
<text x="24" y="2586" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> authz/MessageAuthorizationPolicy.java:90 public Map&lt;WebSocketMessageType, Set&lt;String&gt;&gt; requirements() {</tspan></text>
<text x="24" y="2608" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketPlatformStartupValidator.java:3 import dev.caskeleton.adapter.inbound.websocket.authz.MessageAuthorizationPolicy;</tspan></text>
<text x="24" y="2630" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketPlatformStartupValidator.java:56 MessageAuthorizationPolicy authorization,</tspan></text>
<text x="24" y="2652" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> config/WebSocketPlatformStartupValidator.java:38 public WebSocketPlatformStartupValidator(boolean productionProfile) {</tspan></text>
<text x="24" y="2674" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> config/WebSocketPlatformStartupValidator.java:53 public void validate(</tspan></text>
<text x="24" y="2696" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> config/WebSocketPlatformStartupValidator.java:106 authorization.undeclaredAmong(</tspan></text>
<text x="24" y="2718" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketPlatformStartupValidatorTest.java:40 new WebSocketPlatformStartupValidator(true);</tspan></text>
<text x="24" y="2740" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> WebSocketPlatformStartupValidatorTest.java:42 new WebSocketPlatformStartupValidator(false);</tspan></text>
<text x="24" y="2762" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="2784" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 리프의 CLAUDE.md 가 적는 범위와 인바운드 정책 절 전체</tspan></text>
<text x="24" y="2806" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:15 - Opt-in STOMP over SockJS transport configuration and typed validation.</tspan></text>
<text x="24" y="2828" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:16 - HTTP-handshake principal enforcement and client-inbound STOMP destination authorization.</tspan></text>
<text x="24" y="2850" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:17 - Fixed client-safe STOMP `ERROR` mapping.</tspan></text>
<text x="24" y="2872" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:18 - Best-effort in-process event push through an explicit `LiveEventProjector` allowlist.</tspan></text>
<text x="24" y="2894" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:52 ## Inbound policy</tspan></text>
<text x="24" y="2916" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:53 </tspan></text>
<text x="24" y="2938" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:54 - The HTTP upgrade must already have a nonblank `Principal`; the adapter does not authenticate</tspan></text>
<text x="24" y="2960" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:55 credentials itself.</tspan></text>
<text x="24" y="2982" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:56 - STOMP `SUBSCRIBE` is allowed only for the configured broadcast destination.</tspan></text>
<text x="24" y="3004" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:57 - Authenticated `SEND` is allowed only below `/app/**`.</tspan></text>
<text x="24" y="3026" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:58 - Client `SEND` to `/topic/**` and other destinations is rejected.</tspan></text>
<text x="24" y="3048" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:59 - Every client-visible processing failure becomes the fixed</tspan></text>
<text x="24" y="3070" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> websocket/CLAUDE.md:60 `WEBSOCKET_REQUEST_REJECTED` ERROR code with an empty payload.</tspan></text>
</svg>