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>
114 lines
18 KiB
XML
114 lines
18 KiB
XML
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1844" viewBox="0 0 1200 1844" 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="1842" 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">$ L=src/adapter/outbound/cache-redis
|
|
R=$L/src/main/java/dev/caskeleton/adapter/outbound/cache/redis
|
|
C=$R/sdk/config/Redi</tspan><tspan x="24" dy="22">sSdkAutoConfiguration.java
|
|
M=$L/src/main/java
|
|
|
|
echo '# 표가 없다고 적은 자리를 센다'
|
|
echo -n '# sdk/lettuce/connection</tspan><tspan x="24" dy="22"> : '
|
|
echo "$(find $R/sdk/lettuce/connection -name '*.java' | wc -l) 파일 $(find $R/sdk/lettuce/connection -name '*.java'</tspan><tspan x="24" dy="22"> -exec cat {} + | wc -l) 줄"
|
|
find $R/sdk/lettuce/connection -name '*.java' -exec wc -l {} + | sort -rn | sed -n '2,5p' |</tspan><tspan x="24" dy="22"> sed "s#$R/# #"
|
|
echo '# 표가 든 다섯 포트 중 구현이 있는 넷'
|
|
for p in cache idempotency ratelimit lease; do
|
|
print</tspan><tspan x="24" dy="22">f ' %-12s %s 파일 %s 줄\n' "$p" "$(find $R/$p -name '*.java' | wc -l)" "$(find $R/$p -name '*.java' -exec cat {} + | </tspan><tspan x="24" dy="22">wc -l)"
|
|
done
|
|
echo -n ' 합계 '
|
|
echo "$(find $R/cache $R/idempotency $R/ratelimit $R/lease -name '*.java' | wc </tspan><tspan x="24" dy="22">-l) 파일 $(find $R/cache $R/idempotency $R/ratelimit $R/lease -name '*.java' -exec cat {} + | wc -l) 줄"
|
|
echo -n '# 다</tspan><tspan x="24" dy="22">섯째 session : '
|
|
ls -d $R/session 2>/dev/null || { echo -n '패키지 없음, main 전체에서 session 문자열 '; grep -rn 'sessi</tspan><tspan x="24" dy="22">on' -i --include=*.java $M | wc -l; }
|
|
echo -n '# 그 행에 이름이 없어 뺀 패키지 : '
|
|
echo "realtime $(find $R/realtime -</tspan><tspan x="24" dy="22">name '*.java' -exec cat {} + | wc -l) 줄, keyspace $(find $R/keyspace -name '*.java' -exec cat {} + | wc -l) 줄"
|
|
echo -n</tspan><tspan x="24" dy="22"> '# 상태 기여자 : '
|
|
echo "$(wc -l < $R/sdk/config/RedisHealthContributor.java) + $(wc -l < $R/sdk/config/RedisCorrectn</tspan><tspan x="24" dy="22">essRoles.java)"
|
|
|
|
echo
|
|
echo '# 조립되지 않는다고 적힌 자동 설정의 @Bean 메서드'
|
|
grep -n '@Bean' -A2 "$C" | grep -E '@Bean</tspan><tspan x="24" dy="22">|@Conditional|public ' | grep -v '^55:' | sed 's/^/ /'
|
|
|
|
echo
|
|
echo '# build.gradle 이 등록한 간선 셋과 앞 주석'
|
|
sed -n</tspan><tspan x="24" dy="22"> '7,15p' $L/build.gradle
|
|
|
|
echo
|
|
echo '# 그 간선들을 임포트하는 파일'
|
|
for e in application shared adapter.outbound.suppo</tspan><tspan x="24" dy="22">rt; do
|
|
printf ' dev.caskeleton.%-24s %s 파일\n' "$e" "$(grep -rl "^import dev.caskeleton.$e" $M | wc -l)"
|
|
done
|
|
echo</tspan><tspan x="24" dy="22"> -n '# 주석의 주어인 sdk 패키지만 보면 : '
|
|
grep -rl 'dev.caskeleton.application\|dev.caskeleton.shared' $R/sdk | wc -l</tspan><tspan x="24" dy="22">
|
|
|
|
echo
|
|
echo '# 스무 줄 뒤의 별개 주석'
|
|
sed -n '33,39p' $L/build.gradle</tspan></text>
|
|
<text x="24" y="464" 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="486" 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-02T10:44:47+00:00 · exit 0</tspan></text>
|
|
<line x1="24" y1="500" x2="1176" y2="500" stroke="#30363d"/>
|
|
<text x="24" y="540" 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="562" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># sdk/lettuce/connection : 9 파일 1473 줄</tspan></text>
|
|
<text x="24" y="584" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 600 sdk/lettuce/connection/RedisTopologyClientFactory.java</tspan></text>
|
|
<text x="24" y="606" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 316 sdk/lettuce/connection/RedisRuntimeOwner.java</tspan></text>
|
|
<text x="24" y="628" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 153 sdk/lettuce/connection/SentinelFailoverObserver.java</tspan></text>
|
|
<text x="24" y="650" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 142 sdk/lettuce/connection/RedisConnectionRegistry.java</tspan></text>
|
|
<text x="24" y="672" 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="694" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> cache 2 파일 641 줄</tspan></text>
|
|
<text x="24" y="716" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> idempotency 2 파일 835 줄</tspan></text>
|
|
<text x="24" y="738" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> ratelimit 3 파일 516 줄</tspan></text>
|
|
<text x="24" y="760" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> lease 2 파일 606 줄</tspan></text>
|
|
<text x="24" y="782" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 합계 9 파일 2598 줄</tspan></text>
|
|
<text x="24" y="804" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 다섯째 session : 패키지 없음, main 전체에서 session 문자열 8</tspan></text>
|
|
<text x="24" y="826" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 행에 이름이 없어 뺀 패키지 : realtime 591 줄, keyspace 106 줄</tspan></text>
|
|
<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"># 상태 기여자 : 88 + 62</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"></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"># 조립되지 않는다고 적힌 자동 설정의 @Bean 메서드</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"> 83: @Bean</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"> 85- public RedisSdkSettings redisSdkSettings() {</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"> 101: @Bean</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"> 102- public RedisSdkSettingsValidation redisSdkSettingsValidation(</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"> 156: @Bean</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"> 157- public RedisResolvedCredentials redisResolvedCredentials(</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"> 226: @Bean</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"> 227- public RedisRuntimeClient redisRuntimeClient(</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"> 273: @Bean(destroyMethod = "close")</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"> 274- public RedisRuntimeOwner redisRuntimeOwner(RedisRuntimeClient client, RedisSdkSettings settings) {</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"> 297: @Bean(RedisCorrectnessRoles.OPTIONAL_HEALTH_CONTRIBUTOR)</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"> 298- public HealthIndicator redisOptional(RedisRuntimeOwner owner, RedisSdkSettings settings) {</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"> 324: @Bean(RedisCorrectnessRoles.REQUIRED_HEALTH_CONTRIBUTOR)</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"> 325- @Conditional(RedisCorrectnessRoleBound.class)</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"> 326- public HealthIndicator redisRequired(RedisRuntimeOwner owner, RedisSdkSettings settings) {</tspan></text>
|
|
<text x="24" y="1244" 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="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># build.gradle 이 등록한 간선 셋과 앞 주석</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"> // Registered edges the semantic port adapters need. The SDK itself imports nothing from them</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"> // today (0 imports across main source) — the semantic cache/session/idempotency/rate-limit</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"> // adapters that did were removed and are restored by Phase E of</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"> // docs/superpowers/plans/2026-08-10-redis-optionality-and-composition.md. They stay declared</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"> // because that restoration is the module's stated responsibility, not because anything here</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"> // compiles against them.</tspan></text>
|
|
<text x="24" y="1420" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> implementation project(':application-core')</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"> implementation project(':shared-contract')</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"> implementation project(':adapter:outbound:support')</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"></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"># 그 간선들을 임포트하는 파일</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"> dev.caskeleton.application 7 파일</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"> dev.caskeleton.shared 3 파일</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"> dev.caskeleton.adapter.outbound.support 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"># 주석의 주어인 sdk 패키지만 보면 : 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"></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"># 스무 줄 뒤의 별개 주석</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"> // Deliberately absent:</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"> // org.springframework.data:spring-data-redis — the SDK owns its own typed API and command</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"> // policy on purpose; routing through Spring Data would reintroduce the untyped, unguarded</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"> // command surface the catalog exists to prevent. Zero imports.</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"> // io.micrometer:micrometer-core — observation leaves this leaf as RedisObservation through a</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"> // Consumer sink; binding it to a meter registry belongs to the composition root, not here.</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"> // Zero imports.</tspan></text>
|
|
</svg>
|