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>
6.6 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | analysis-finding-a19-f019 | admin 스위치가 가드를 켜고 서비스는 켜지 않는다 | messaging-and-outbox | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a19-f019 | 2026-09-01 | case-analysis-finding-a19-f019.body.md |
|
|
|
admin 스위치가 가드를 켜고 서비스는 켜지 않는다
관리 스위치를 켜면 빈 넷이 만들어진다. 전부 가드와 기록과 검증기다. 그중 관리 서비스 인터페이스의 유일한 구현과 승인 검증자의 유일한 구현은 만들어지지 않는다. 부재가 문서화된 것은 하나뿐이다.
관계
- 관리 계약 leaf는 main 25파일 1613줄에 테스트 파일이 1개다 같은 하위 범위의 다른 사례다.
- 멱등 실행 계층과 durable-operation 표면이 픽스처에서만 조립된다 다른 가족의 같은 형태다.
- 켰다고 생각한 기능이 없다는 것을 사고 한가운데에서 발견한다 이 사례가 그 규칙의 형태다.
문제
관리 스위치를 켜면 무엇이 만들어지는지 확인했다.
결론
빈 넷이다.
파괴적 연산 가드와 관리 연산 기록, 관리 내구성 검증기, 그리고 위상 조사기가 있을 때 합성 위상 검증기다.
만들지 않는 것이 다섯이다.
파괴적 관리자는 주 참조가 둘이고 부재가 자바독에 명시되어 있다.
기본 관리 서비스는 주 참조가 0 이고 부재 이유가 없다.
해시 기반 승인 검증자도 주 참조가 0 이고 테스트가 넷이다. 부재 이유가 없다.
위상 검증 실행체도 0 이고 이유가 없다.
재구동 서비스와 재생 서비스도 각각 주 참조가 있고 이유가 없다.
기본 관리 서비스는 관리 서비스 인터페이스의 유일한 구현이다.
즉 관리 평면을 켜도 관리 서비스가 없다.
해시 기반 승인 검증자는 승인 검증자의 유일한 구현이고, 테스트 넷이 그것을 검증한다. 위조 테스트도 포함된다.
승인된 재구동 계획과 재생 계획과 검증된 승인과 계획 요약으로 이루어진 승인 사슬 전체가 검증자 없이는 시작될 수 없다.
부재의 등급이 넷 다 다르지 않은데 문서화는 하나만 됐다.
파괴적 관리자의 부재에는 명확한 이유가 있다. 이 실행체는 관리 자격증명을 갖지 않는다는 것이다.
나머지 넷에는 이유가 적혀 있지 않고, 그중 둘은 파괴적이지 않은 관리 동작에 필요한 것이다. 재구동과 재생의 승인과 실행이다.
실패 시나리오는 이렇다.
운영 절차서에 따라 사고 대응 중 재구동을 실행하려 한다.
관리 스위치를 켠다. 부팅은 성공하고 가드와 기록과 내구성 검증기가 올라온다.
그런데 관리 서비스 빈이 없으므로 재구동을 호출할 대상이 없다.
사고 한가운데에서 켰다고 생각한 기능이 없다는 것을 발견한다.
내구성 검증기의 자바독이 경계한 상황과 정확히 같은 시점이다. 그 간극은 그 연산을 돌리게 만든 사고 도중에만 드러나며 그것이 발견하기에 가장 나쁜 순간이라는 것이다.
판정은 P2 다.
검증 환경
Spring Boot : 4.0.8 확인 방식 : 자동 설정 빈 목록과 타입별 참조 계수 소스 수정 : x
재현 조건
원문은 final/evidence/raw/252 계열에 있다.
- 관리 자동 설정의 조건과 빈 목록을 읽는다.
- 관리 평면에 필요한 타입 목록을 만든다.
- 각 타입의 주 참조를 센다.
- 각 타입의 부재가 문서화되었는지 확인한다.
- 관리 서비스 인터페이스의 구현을 센다.
본문
app.messaging.admin.enabled=true가 만드는 bean은 넷이다 — DestructiveOperationGuard, AdminOperationJournal, MessagingAdminDurabilityValidator, (BrokerTopologyInspector가 있을 때) CompositeTopologyValidator.
DestructiveOperationGuard 참조 위치
:::evidence key="analysis-finding-a19-f019" alt="코드베이스에서 DestructiveOperationGuard 를 검색한 출력 6줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DestructiveOperationGuard 코드베이스 검색 — 6줄 · exit 0" zoom="true" :::
만들지 않는 것
| 타입 | leaf | main 참조 | 부재가 문서화됐는가 |
|---|---|---|---|
DestructiveMessagingAdmin |
admin-runtime | 2 | 예 — javadoc이 명시 |
DefaultMessagingAdminService |
admin-runtime | 0 | 아니오 |
HmacApprovalVerifier |
admin-api | 0 (test 4) | 아니오 |
TopologyValidationRuntime |
admin-runtime | 0 | 아니오 |
RedriveService / ReplayService |
admin-runtime | 2 / 1 | 아니오 |
DefaultMessagingAdminService는 MessagingAdminService(인터페이스, main 참조 2)의 유일한 구현이다. 즉 admin plane을 켜도 admin 서비스가 없다. HmacApprovalVerifier는 ApprovalVerifier의 유일한 구현이고, ApprovedRedrivePlan/ApprovedReplayPlan/VerifiedApproval/PlanDigest(main 참조 10)로 이루어진 승인 사슬 전체가 검증자 없이는 시작될 수 없다.
부재의 등급이 다르지 않은데 문서화는 하나만 됐다
DestructiveMessagingAdmin의 부재에는 명확한 이유가 있다("이 런타임은 admin 자격 증명을 갖지 않는다"). 나머지 넷에는 이유가 적혀 있지 않고, 그중 둘은 파괴적이지 않은 admin 동작(redrive/replay의 승인·실행)에 필요한 것이다.
실패 시나리오
운영 절차서(docs/messaging/retry-dlq-redrive.md)에 따라 사고 대응 중 redrive를 실행하려 한다. app.messaging.admin.enabled=true로 켠다. 부팅은 성공하고 가드·journal·durability 검증기가 올라온다. 그런데 MessagingAdminService bean이 없으므로 redrive를 호출할 대상이 없다 — MessagingAdminDurabilityValidator의 javadoc이 경계한 상황("the gap only shows up during the incident the operation was run to resolve, which is the worst possible moment to discover it")과 정확히 같은 시점이다. P2.
확인하지 못한 것
관리 스위치를 켜고 재구동을 호출해 대상이 없는 것을 재현하지 않았다. 빈 목록상 그 결과가 나온다.