Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/a17-f002-stomp.txt
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

99 lines
7.5 KiB
Plaintext

# ca-skeleton.websocket 어댑터가 등록하는 인터셉터 둘
AuthenticatedHandshakeInterceptor.java 34 줄
WebSocketInboundAuthorizationInterceptor.java 53 줄
stomp/WebSocketConfig.java:32 @ConditionalOnProperty(prefix = "ca-skeleton.websocket", name = "enabled", havingValue = "true")
stomp/WebSocketConfig.java:36 private final AuthenticatedHandshakeInterceptor handshakeInterceptor =
stomp/WebSocketConfig.java:37 new AuthenticatedHandshakeInterceptor();
stomp/WebSocketConfig.java:40 private final WebSocketInboundAuthorizationInterceptor inboundAuthorization;
stomp/WebSocketConfig.java:44 this.inboundAuthorization = new WebSocketInboundAuthorizationInterceptor(properties);
stomp/WebSocketConfig.java:53 .addInterceptors(handshakeInterceptor)
stomp/WebSocketConfig.java:58 public void configureClientInboundChannel(ChannelRegistration registration) {
# 같은 채널에 붙는 세 번째 인터셉터와 그 게이트
advanced/stomp/StompSecurityInterceptor.java 96 줄
advanced/stomp/StompConfiguration.java:24 @ConditionalOnProperty(
advanced/stomp/StompConfiguration.java:25 prefix = "app.websocket-platform.advanced.stomp",
advanced/stomp/StompConfiguration.java:26 name = "enabled",
advanced/stomp/StompConfiguration.java:27 havingValue = "true")
advanced/stomp/StompConfiguration.java:33 public StompConfiguration(StompProfile profile, StompSecurityInterceptor securityInterceptor) {
advanced/stomp/StompConfiguration.java:35 this.securityInterceptor = securityInterceptor;
advanced/stomp/StompConfiguration.java:39 public void configureClientInboundChannel(ChannelRegistration registration) {
advanced/stomp/StompConfiguration.java:40 registration.interceptors(securityInterceptor);
# 이 모듈이 출하 런타임 그래프에 있는가
app-bootstrap/build.gradle:196 // Explicit qualification-only composition. These projects remain absent from main
app-bootstrap/build.gradle:197 // api/implementation/compileOnly/runtimeOnly and therefore from both shipped runtime graphs.
app-bootstrap/build.gradle:198 conditionalTransportTestImplementation project(':adapter:inbound:graphql')
app-bootstrap/build.gradle:199 conditionalTransportTestImplementation project(':adapter:inbound:grpc')
app-bootstrap/build.gradle:200 conditionalTransportTestImplementation project(':adapter:inbound:websocket')
websocket/CLAUDE.md:21 The module is absent from both shipped runtime compositions. Being registered and tested does not
websocket/CLAUDE.md:22 activate it. A future composition must deliberately add the registered dependency and set
websocket/CLAUDE.md:23 `ca-skeleton.websocket.enabled=true` with explicit safe origins.
# 정책 계층 — 네 패키지의 파일과 선언 형태와 줄 수
security WebSocketAuthenticationProfile.java enum 57 줄
security WebSocketConnectionTicket.java record 77 줄
security WebSocketOriginPolicy.java final class 116 줄
security WebSocketTicketStore.java interface 32 줄
authz MessageAuthorizationPolicy.java final class 93 줄
idempotency CommandReconciliation.java interface 84 줄
idempotency CommittedResultLedger.java interface 61 줄
idempotency WebSocketCommandKey.java record 40 줄
idempotency WebSocketCommandOutcome.java enum 33 줄
budget WebSocketConnectionBudget.java record 103 줄
합계 10 파일
# 그 열 타입을 stomp 패키지가 참조하는가, 그리고 어디서 생성되는가
CommandReconciliation stomp참조=0 main생성=0 같은파일에@Bean있는main=0
CommittedResultLedger stomp참조=0 main생성=0 같은파일에@Bean있는main=0
MessageAuthorizationPolicy stomp참조=0 main생성=1 같은파일에@Bean있는main=0
WebSocketAuthenticationProfile stomp참조=0 main생성=0 같은파일에@Bean있는main=0
WebSocketCommandKey stomp참조=0 main생성=0 같은파일에@Bean있는main=0
WebSocketCommandOutcome stomp참조=0 main생성=0 같은파일에@Bean있는main=0
WebSocketConnectionBudget stomp참조=0 main생성=1 같은파일에@Bean있는main=0
WebSocketConnectionTicket stomp참조=0 main생성=0 같은파일에@Bean있는main=0
WebSocketOriginPolicy stomp참조=0 main생성=1 같은파일에@Bean있는main=1
WebSocketTicketStore stomp참조=0 main생성=0 같은파일에@Bean있는main=0
# 원문이 22 로 적은 수의 출처
analysis/17:235:# Sub-scope 04 — `security` + `authz` + `idempotency` + `budget` + `error` + `observability` + `admin` + `release` (31 files, main 22 + test 9)
security 4
authz 1
idempotency 4
budget 1
error 5
observability 2
admin 2
release 3
여덟 패키지 합계 22
# MessageAuthorizationPolicy 를 받는 자리와 그 받는 쪽의 생성자
authz/MessageAuthorizationPolicy.java:26 public final class MessageAuthorizationPolicy {
authz/MessageAuthorizationPolicy.java:39 public static MessageAuthorizationPolicy of(Map<WebSocketMessageType, Set<String>> requirements) {
authz/MessageAuthorizationPolicy.java:57 public boolean permits(WebSocketMessageType type, Set<String> heldAuthorities) {
authz/MessageAuthorizationPolicy.java:70 public boolean declares(WebSocketMessageType type) {
authz/MessageAuthorizationPolicy.java:81 public java.util.List<WebSocketMessageType> undeclaredAmong(
authz/MessageAuthorizationPolicy.java:90 public Map<WebSocketMessageType, Set<String>> requirements() {
WebSocketPlatformStartupValidator.java:3 import dev.caskeleton.adapter.inbound.websocket.authz.MessageAuthorizationPolicy;
WebSocketPlatformStartupValidator.java:56 MessageAuthorizationPolicy authorization,
config/WebSocketPlatformStartupValidator.java:38 public WebSocketPlatformStartupValidator(boolean productionProfile) {
config/WebSocketPlatformStartupValidator.java:53 public void validate(
config/WebSocketPlatformStartupValidator.java:106 authorization.undeclaredAmong(
WebSocketPlatformStartupValidatorTest.java:40 new WebSocketPlatformStartupValidator(true);
WebSocketPlatformStartupValidatorTest.java:42 new WebSocketPlatformStartupValidator(false);
# 리프의 CLAUDE.md 가 적는 범위와 인바운드 정책 절 전체
websocket/CLAUDE.md:15 - Opt-in STOMP over SockJS transport configuration and typed validation.
websocket/CLAUDE.md:16 - HTTP-handshake principal enforcement and client-inbound STOMP destination authorization.
websocket/CLAUDE.md:17 - Fixed client-safe STOMP `ERROR` mapping.
websocket/CLAUDE.md:18 - Best-effort in-process event push through an explicit `LiveEventProjector` allowlist.
websocket/CLAUDE.md:52 ## Inbound policy
websocket/CLAUDE.md:53
websocket/CLAUDE.md:54 - The HTTP upgrade must already have a nonblank `Principal`; the adapter does not authenticate
websocket/CLAUDE.md:55 credentials itself.
websocket/CLAUDE.md:56 - STOMP `SUBSCRIBE` is allowed only for the configured broadcast destination.
websocket/CLAUDE.md:57 - Authenticated `SEND` is allowed only below `/app/**`.
websocket/CLAUDE.md:58 - Client `SEND` to `/topic/**` and other destinations is rejected.
websocket/CLAUDE.md:59 - Every client-visible processing failure becomes the fixed
websocket/CLAUDE.md:60 `WEBSOCKET_REQUEST_REJECTED` ERROR code with an empty payload.