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>
79 lines
5.2 KiB
Plaintext
79 lines
5.2 KiB
Plaintext
# 주제: 네 인증 레인이 실제로 돌고 통과하지만, 그 결과를 릴리스 게이트에 먹이는 것이 없다
|
|
# revision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
# severity: P3
|
|
|
|
# ---- 먼저: 이 블록의 상태는 이미 정확히 문서화되어 있다 ----
|
|
# src/grpc/CLAUDE.md:74-86 "Runtime membership이 비어 있다는 것의 의미"
|
|
# "registry의 모든 grpc:* leaf는 현재 runtime_memberships가 비어 있다. messaging:*가 처음
|
|
# 착지했을 때와 같은 상태다:
|
|
# - **배포 아티팩트가 싣고 있지 않다.** build-only다.
|
|
# - 애플리케이션에 배선하려면 registry의 runtime_memberships를 먼저 바꾸고
|
|
# verifyRuntimeModuleMembership을 통과시켜야 한다.
|
|
# - **런타임에 있다는 것과 자격이 증명됐다는 것은 다르다.** 현재 in-process·Netty·fault lane은
|
|
# 실제로 실행되어 통과하지만, 실제 배포 환경에서의 soak·performance baseline은 없다.
|
|
# **GrpcStableReleaseGate가 그 구분을 문서가 아니라 코드로 붙들고 있다.**"
|
|
# src/grpc/CLAUDE.md:88-98
|
|
# "messaging:*의 MSG-015(bridge 부재)를 반복하지 않는 것이 이 family의 목표다."
|
|
# "현재 adapter:inbound:grpc는 이 family에 의존하지 않는다 — registry의 allowed_dependencies를
|
|
# 보라. 배선은 별도 결정이며, 그때 registry edge와 브리지가 함께 들어간다."
|
|
# => EVD-325 가 측정한 것은 전부 이 문서가 이미 말한 것이다. 드리프트가 아니라 확인이다.
|
|
|
|
# ---- 네 레인을 실제로 돌렸다 ----
|
|
# command: ./gradlew :grpc:grpc-testkit:grpcInProcessContractTest :…:grpcNettyContractTest
|
|
# :…:grpcFaultTest :…:grpcPerformanceTest --console=plain
|
|
# exit: 0 (BUILD SUCCESSFUL, 4 executed)
|
|
grpcInProcessContractTest tests=7 failures=0
|
|
grpcNettyContractTest tests=9 failures=0 <- 실제 HTTP/2 + TLS, ephemeral socket
|
|
grpcFaultTest tests=9 failures=0
|
|
grpcPerformanceTest tests=1 failures=0
|
|
# 레인 정의(build.gradle):
|
|
# grpc-inprocess "In-process contract lane: adapter, interceptor order, status, idempotency
|
|
# replay. Never HTTP/2, TLS or transport-limit evidence."
|
|
# grpc-netty "Real Netty lane: HTTP/2 on an ephemeral socket, TLS/mTLS, metadata and
|
|
# message hard limits, GOAWAY, keepalive and drain."
|
|
# grpc-fault "Fault-injection lane: connection loss at each evidence boundary, and the
|
|
# classifier that refuses to infer NOT_SENT from an unobserved state."
|
|
# grpc-performance (test 에서 제외) "…against a recorded baseline."
|
|
# build.gradle 주석:
|
|
# "A lane that discovers nothing fails, and none of them can serve an up-to-date result —
|
|
# which is the property the split was protecting."
|
|
|
|
# ---- 그러나 게이트는 레인 결과를 받지 않는다 ----
|
|
# GrpcStableReleaseGate.evaluate 시그니처:
|
|
# public GrpcReleaseDecision evaluate(
|
|
# GrpcReleaseEvidence evidence,
|
|
# Map<String, Boolean> laneResults, <- **호출자가 손으로 넘긴다**
|
|
# GrpcSchemaArtifactPublisher.PublishDecision schemaDecision)
|
|
#
|
|
# command: git grep -n "GrpcStableReleaseGate|laneResults" -- src
|
|
# GrpcCompatibilityMatrix.java:76-84 missingResults(Map<String,Boolean> laneResults)
|
|
# GrpcStableReleaseGateTest.java:16,18-19,136-137 <- 유일한 호출부, 테스트
|
|
# src/grpc/CLAUDE.md:82 <- 문서 언급
|
|
# => 프로덕션 호출부 0. 네 레인의 JUnit XML 을 읽어 laneResults 를 만드는 Gradle 태스크나 코드가 없다.
|
|
|
|
# ---- messaging 과의 대비 ----
|
|
# messaging (EVD-318 에서 실행 확인):
|
|
# messagingCertificationTest -> broker-certification-evidence.jsonl (커밋됨)
|
|
# CertifiedEvidence.load() -> 매니페스트를 읽는다
|
|
# CompatibilityMatrix.Entry.hasLiveBrokerCertification() -> 매니페스트에서 **파생**
|
|
# verifyMessagingCertificationEvidence -> 생산물 vs 커밋본 비교, 불일치면 빌드 실패
|
|
# => 닫힌 루프. CompatibilityMatrix javadoc: "an adapter cannot be promoted by editing a boolean"
|
|
#
|
|
# grpc:
|
|
# 4개 레인 -> JUnit XML (build/test-results/<lane>/)
|
|
# ??? -> Map<String, Boolean> laneResults
|
|
# GrpcStableReleaseGate.evaluate(...) <- 테스트만 호출
|
|
# => 열린 루프. laneResults 는 호출자가 편집할 수 있는 불리언 맵이다.
|
|
#
|
|
# 즉 messaging-testkit 이 명시적으로 고친 결함 형태 —
|
|
# "an author added a line and the compatibility matrix … all believed it, on a machine that may
|
|
# never have started a container" (CertifiedEvidence javadoc)
|
|
# — 와 같은 모양이 grpc 쪽 게이트에 남아 있다. 다만 grpc 쪽은 레인이 실제로 돌고,
|
|
# 아무도 게이트를 부르지 않으므로 잘못된 주장이 발행되지도 않는다.
|
|
|
|
# ---- 판정 ----
|
|
# CLAUDE.md:82 의 "GrpcStableReleaseGate가 그 구분을 문서가 아니라 코드로 붙들고 있다" 는
|
|
# 절반만 참이다. 구분은 **타입으로** 붙들려 있다(GrpcEvidenceGrade, GrpcCompatibilityMatrix,
|
|
# requireCertified). 그러나 그 타입에 실제 레인 결과를 넣는 코드가 없으므로,
|
|
# 게이트는 아직 아무것도 붙들고 있지 않다.
|