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>
15 lines
4.5 KiB
JSON
15 lines
4.5 KiB
JSON
{
|
|
"assetKey": "a17-f002-stomp",
|
|
"kind": "terminal",
|
|
"command": "set -e\nset -o pipefail\nW=src/adapter/inbound/websocket/src/main/java/dev/caskeleton/adapter/inbound/websocket\nstrip() { sed -E 's#^src/[^:]*/websocket/([^:]*/)?([^:/]+):([0-9]+):# \\2:\\3 #'; }\ncut_range() { awk -v f=\"$2\" -v a=\"$3\" -v b=\"$4\" 'NR>=a && NR<=b {printf \" %s:%d %s\\n\", f, NR, $0}' \"$1\"; }\n\necho \"# ca-skeleton.websocket 어댑터가 등록하는 인터셉터 둘\"\nfor f in AuthenticatedHandshakeInterceptor WebSocketInboundAuthorizationInterceptor; do\n printf \" %-44s %s 줄\\n\" \"$f.java\" \"$(wc -l < $W/stomp/$f.java)\"\ndone\nawk 'NR>=30 && NR<=60 && (/Interceptor/ || /addInterceptors/ || /configureClientInboundChannel/ || /ConditionalOnProperty/) {printf \" stomp/WebSocketConfig.java:%d %s\\n\", NR, $0}' $W/stomp/WebSocketConfig.java\n\necho\necho \"# 같은 채널에 붙는 세 번째 인터셉터와 그 게이트\"\nprintf \" %-44s %s 줄\\n\" \"advanced/stomp/StompSecurityInterceptor.java\" \"$(wc -l < $W/advanced/stomp/StompSecurityInterceptor.java)\"\ncut_range $W/advanced/stomp/StompConfiguration.java advanced/stomp/StompConfiguration.java 24 27\nawk 'NR>=33 && NR<=41 && (/securityInterceptor/ || /configureClientInboundChannel/) {printf \" advanced/stomp/StompConfiguration.java:%d %s\\n\", NR, $0}' $W/advanced/stomp/StompConfiguration.java\n\necho\necho \"# 이 모듈이 출하 런타임 그래프에 있는가\"\nawk 'NR>=196 && NR<=200 {printf \" app-bootstrap/build.gradle:%d %s\\n\", NR, $0}' src/app-bootstrap/build.gradle\ncut_range src/adapter/inbound/websocket/CLAUDE.md websocket/CLAUDE.md 21 23\n\necho\necho \"# 정책 계층 — 네 패키지의 파일과 선언 형태와 줄 수\"\nfor d in security authz idempotency budget; do\n for f in $(find $W/$d -name '*.java' | sort); do\n k=$(grep -oE 'public (final class|record|enum|interface|class)' $f | head -1 || true)\n printf \" %-12s %-38s %-16s %s 줄\\n\" \"$d\" \"$(basename $f)\" \"${k#public }\" \"$(wc -l < $f)\"\n done\ndone\nprintf \" 합계 %s 파일\\n\" \"$(find $W/security $W/authz $W/idempotency $W/budget -name '*.java' | wc -l)\"\n\necho\necho \"# 그 열 타입을 stomp 패키지가 참조하는가, 그리고 어디서 생성되는가\"\nfor f in $(find $W/security $W/authz $W/idempotency $W/budget -name '*.java' | sed 's|.*/||;s|\\.java$||' | sort); do\n s=$(grep -rl \"\\b$f\\b\" $W/stomp 2>/dev/null | wc -l || true)\n n=$(git grep -c \"new $f(\" -- 'src/**/*.java' 2>/dev/null | grep '/main/' | wc -l || true)\n b=$(git grep -l \"$f \" -- 'src/**/*.java' 2>/dev/null | grep '/main/' | xargs grep -l '@Bean' 2>/dev/null | wc -l || true)\n printf \" %-34s stomp참조=%s main생성=%s 같은파일에@Bean있는main=%s\\n\" \"$f\" \"$s\" \"$n\" \"$b\"\ndone\n\necho\necho \"# 원문이 22 로 적은 수의 출처\"\ngrep -n '^# Sub-scope 04' /shared/document-detail/clean-architecture-backend-template/analysis/17-adapter-inbound-websocket.md \\\n | cut -c1-150 | sed 's#^# analysis/17:#'\nt=0; for d in security authz idempotency budget error observability admin release; do\n n=$(find $W/$d -name '*.java' | wc -l); t=$((t+n)); printf \" %-14s %s\\n\" \"$d\" \"$n\"\ndone\necho \" 여덟 패키지 합계 $t\"\n\necho\necho \"# MessageAuthorizationPolicy 를 받는 자리와 그 받는 쪽의 생성자\"\ngrep -n 'public ' $W/authz/MessageAuthorizationPolicy.java | sed -E 's#^([0-9]+): *# authz/MessageAuthorizationPolicy.java:\\1 #'\ngit grep -n 'MessageAuthorizationPolicy' -- 'src/**/*.java' | grep '/main/' | grep -v '/MessageAuthorizationPolicy.java' | strip\ngrep -n 'public WebSocketPlatformStartupValidator\\|public void validate\\|undeclaredAmong' $W/config/WebSocketPlatformStartupValidator.java \\\n | sed -E 's#^([0-9]+): *# config/WebSocketPlatformStartupValidator.java:\\1 #'\ngit grep -n 'new WebSocketPlatformStartupValidator(' -- 'src/**/*.java' | strip\n\necho\necho \"# 리프의 CLAUDE.md 가 적는 범위와 인바운드 정책 절 전체\"\ncut_range src/adapter/inbound/websocket/CLAUDE.md websocket/CLAUDE.md 15 18\ncut_range src/adapter/inbound/websocket/CLAUDE.md websocket/CLAUDE.md 52 60\n",
|
|
"cwd": "/shared/codebase/clean-architecture-backend-template",
|
|
"exitCode": 0,
|
|
"executedAt": "2026-09-04T00:20:53+00:00",
|
|
"sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916",
|
|
"raw": "evidence/raw/a17-f002-stomp.txt",
|
|
"svg": "evidence/rendered/a17-f002-stomp.svg",
|
|
"rawSha256": "e44e2afd60df7cb8a83b19d83b709b1f8144576d4908e5d9bb90265b5ab4471c",
|
|
"lines": 98,
|
|
"redaction": "코드베이스 측정으로 교체"
|
|
}
|