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>
133 lines
12 KiB
Plaintext
133 lines
12 KiB
Plaintext
Sub-scope 05 (inbound-web) idempotency + operation + operationasync + evidence
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T07:26:46+00:00
|
|
|
|
=== OWNED FILES ===
|
|
09b8372242c82d595ee2f9c3666ca578b676fe27 main/java/dev/caskeleton/adapter/inbound/web/evidence/WebApplicationEvidence.java 44
|
|
2b2d95ea7c6b1c08efa9daa27ea35dd2ce7f8bfe main/java/dev/caskeleton/adapter/inbound/web/evidence/WebEvidenceAxis.java 15
|
|
9d50db0ade99d79433193c095679d079fdb34770 main/java/dev/caskeleton/adapter/inbound/web/evidence/WebExecutionEvidence.java 57
|
|
3a6dc3e2292e7ea77f973bd77a68c3461fbe6a48 main/java/dev/caskeleton/adapter/inbound/web/evidence/WebExecutionEvidenceTracker.java 125
|
|
ea8eed7e43c59f73c57c29219e59977680a52dcc main/java/dev/caskeleton/adapter/inbound/web/evidence/WebRequestPhase.java 44
|
|
269ab58d05e149e802c6d1f1ba8965960b8804f7 main/java/dev/caskeleton/adapter/inbound/web/evidence/WebResponseEvidence.java 46
|
|
401ffe547bfb75201bdcb493ec2ad1b31bbde4fe main/java/dev/caskeleton/adapter/inbound/web/idempotency/DeterministicCommandEncoder.java 74
|
|
e97ca5b43539cc0cbab27197a30f15cdfb41b6be main/java/dev/caskeleton/adapter/inbound/web/idempotency/FingerprintHeaderPolicy.java 102
|
|
6bc27398702703e6296473ce7d5fdbf7a382bc25 main/java/dev/caskeleton/adapter/inbound/web/idempotency/IdempotencyAdmission.java 85
|
|
02e93a8b2b9bf830369021194517d05c8f90383e main/java/dev/caskeleton/adapter/inbound/web/idempotency/IdempotencyKey.java 33
|
|
0dc1a346d1d8d51249f39a1069ecea0d1f669864 main/java/dev/caskeleton/adapter/inbound/web/idempotency/IdempotencyKeySupport.java 66
|
|
3daf9dbddd62b03584f1e3d675eae8253b6a3b03 main/java/dev/caskeleton/adapter/inbound/web/idempotency/IdempotencyResponsePlan.java 97
|
|
e6563e02b7814ec4374cb66a67d2ea756672fa5c main/java/dev/caskeleton/adapter/inbound/web/idempotency/JsonIdempotentResponseCodec.java 44
|
|
bcdecedf0ae006aceb630d4377fc020ac3bf7598 main/java/dev/caskeleton/adapter/inbound/web/idempotency/SemanticRequestFingerprintFactory.java 94
|
|
d4c5c53c1ef4609e56b1c2057ae673920512c83c main/java/dev/caskeleton/adapter/inbound/web/idempotency/WebIdempotencyGate.java 180
|
|
7a0ba511685fb7086e06f62eb8aa79ca139ccd0a main/java/dev/caskeleton/adapter/inbound/web/mvc/idempotency/IdempotentResponseWriter.java 84
|
|
aceb3928ef9d8321e1c130a8ee0a4536bbe7d43e main/java/dev/caskeleton/adapter/inbound/web/mvc/idempotency/WebMvcIdempotentInvoker.java 108
|
|
0f5c4dfde6c99cf4e5f762f1cd962a07465e26fd main/java/dev/caskeleton/adapter/inbound/web/mvc/operation/OperationHttpController.java 105
|
|
f333b4eae4f8169f71a6b1e46088f199c416ceef main/java/dev/caskeleton/adapter/inbound/web/operation/AdmissionProfileName.java 35
|
|
6a9b1ebfadd3b7fad907d6b4d3849af2229e83cb main/java/dev/caskeleton/adapter/inbound/web/operation/AuthorizationProfileName.java 35
|
|
d9715d2f988d62a1630000dad36f7f9e52a989c1 main/java/dev/caskeleton/adapter/inbound/web/operation/CachePolicyName.java 35
|
|
1d36292c292382371d5ebed1b32b0b1fe619592c main/java/dev/caskeleton/adapter/inbound/web/operation/HttpMethodSemantic.java 54
|
|
3a0f6f5784d856454b7b5ff8778f07db12fcaf8f main/java/dev/caskeleton/adapter/inbound/web/operation/IdempotencyPolicy.java 20
|
|
dc03e5d24fa694498053cc7ce988182507d761cc main/java/dev/caskeleton/adapter/inbound/web/operation/InMemoryWebOperationCatalog.java 61
|
|
826c260984a2d3de253cb01aaf3ed82cd64528c2 main/java/dev/caskeleton/adapter/inbound/web/operation/MutationKind.java 38
|
|
916ad6adb9ae107737789fb43efd472ba56852da main/java/dev/caskeleton/adapter/inbound/web/operation/PreconditionPolicy.java 21
|
|
1c17d83ecbe49279203ac45eb1e16489c879bf47 main/java/dev/caskeleton/adapter/inbound/web/operation/ResponseProfileName.java 35
|
|
53572651a5b29d37b9a6994ab206496b3a2f4ce9 main/java/dev/caskeleton/adapter/inbound/web/operation/WebOperationCatalog.java 28
|
|
225984be514179238b0740670f724ee6ce662a9a main/java/dev/caskeleton/adapter/inbound/web/operation/WebOperationProfile.java 119
|
|
1bc8022b2b9b08660c06c66b29c7c3f28c2791ba main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationAccessPolicy.java 42
|
|
ee641d0b4f594fb8cc82b4f7886d834153c76b37 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationId.java 29
|
|
1c2a0d2dc8569e4af92ccd7a1fe9de8368ab54d8 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationProgress.java 50
|
|
ba9351e26f8dbf3f3f8d1d8cd81fcc49bf48aece main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationQueryService.java 74
|
|
37a69e38dd86724c908e5e347bf29856b453f45c main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationResource.java 159
|
|
441aaca5389c99c568f16eefe7eeda13066695d8 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationResourceFactory.java 112
|
|
c3c340fe86f9f816711d42988ddb8b8cba101484 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationResponse.java 56
|
|
00453ac2722e568d28d4e33d50db2fdb99dfc1e4 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationResponseMapper.java 43
|
|
fd905407b7c0ca864c80eccc3f7d6e988e081b18 main/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationStatus.java 40
|
|
8129f0895798453076bd9af97440617c9f412e20 main/java/dev/caskeleton/adapter/inbound/web/webflux/idempotency/WebFluxIdempotentInvoker.java 168
|
|
23ad8ee4b0a4113872e15374b44ad2efdda0ef42 main/java/dev/caskeleton/adapter/inbound/web/webflux/operation/ReactiveOperationHttpController.java 106
|
|
9dc9f2412a5d6287533e0b85cb49073131a92c57 test/java/dev/caskeleton/adapter/inbound/web/evidence/WebExecutionEvidenceTrackerTest.java 156
|
|
b531bd2a9f21c6c92c66f3e903a462cbdfaa8bf3 test/java/dev/caskeleton/adapter/inbound/web/idempotency/IdempotencyKeySupportTest.java 80
|
|
6b41124404aa453a69157860a914a4a2bdb89398 test/java/dev/caskeleton/adapter/inbound/web/idempotency/InMemoryIdempotencyStore.java 70
|
|
03ddbb79c4c27e9923a5fa30916443b42e4a73a7 test/java/dev/caskeleton/adapter/inbound/web/idempotency/SemanticRequestFingerprintFactoryTest.java 140
|
|
bd2825015460b787bdba7bdc463d38c1e4f33778 test/java/dev/caskeleton/adapter/inbound/web/idempotency/WebIdempotencyGateTest.java 343
|
|
2636c7fb45114d566c58c720fe82a3879b9aa433 test/java/dev/caskeleton/adapter/inbound/web/mvc/idempotency/WebMvcIdempotentInvokerTest.java 288
|
|
93b8a0dcf5ac710ffaa337c7dafe7efdf0c0c06a test/java/dev/caskeleton/adapter/inbound/web/operation/WebOperationCatalogTest.java 141
|
|
787e4a5fbd4b4bc4863e92a590e035ca9668300a test/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationAccessPolicyTest.java 122
|
|
5ea653c8698c2aab6095b1ac45b41a3a3abdcc04 test/java/dev/caskeleton/adapter/inbound/web/operationasync/OperationResourceTest.java 230
|
|
aa70c62a7d7441f545e241e4c48954658731af97 test/java/dev/caskeleton/adapter/inbound/web/webflux/idempotency/WebFluxIdempotentInvokerTest.java 268
|
|
file_count=50
|
|
|
|
=== 8.1 도달성: 멱등성 실행 계층과 durable-operation 표면 ===
|
|
WebIdempotencyGate 5 test 2 testkit
|
|
WebMvcIdempotentInvoker 1 test 1 testkit
|
|
WebFluxIdempotentInvoker 1 test 1 testkit
|
|
IdempotentResponseWriter 1 test 1 testkit
|
|
SemanticRequestFingerprintFactory 6 test 2 testkit
|
|
OperationQueryService 1 testkit
|
|
OperationResourceFactory 1 testkit
|
|
WebExecutionEvidenceTracker 1 main
|
|
$ grep -rn 'inbound.web.idempotency\|WebIdempotencyGate\|IdempotencyKeySupport' --include=*.java app-bootstrap/src/main | wc -l
|
|
0
|
|
exit=0
|
|
|
|
$ grep -rn 'IdempotencyKeySupport' --include=*.java adapter/inbound/web/src/main sample-portfolio/src/main | grep -v IdempotencyKeySupport.java | wc -l
|
|
0
|
|
exit=0
|
|
|
|
=== 8.2 durable-operation 컨트롤러의 게이트와 의존 ===
|
|
$ grep -n 'ConditionalOnProperty' -A 4 adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/mvc/operation/OperationHttpController.java | tail -5
|
|
35:@ConditionalOnProperty(
|
|
36- prefix = "app.web-platform.durable-operations",
|
|
37- name = "enabled",
|
|
38- havingValue = "true")
|
|
39-@RequestMapping(OperationHttpController.BASE_PATH)
|
|
exit=0
|
|
|
|
$ grep -rn 'app.web-platform.durable-operations' --include=*.yml --include=*.yaml --include=*.java . | sed 's|adapter/inbound/web/src/||'
|
|
./main/java/dev/caskeleton/adapter/inbound/web/mvc/operation/OperationHttpController.java:36: prefix = "app.web-platform.durable-operations",
|
|
./main/java/dev/caskeleton/adapter/inbound/web/webflux/operation/ReactiveOperationHttpController.java:37: prefix = "app.web-platform.durable-operations",
|
|
exit=0
|
|
|
|
$ grep -rn 'OperationQueryService' --include=*.java adapter/inbound/web/src/main app-bootstrap/src/main | grep -v 'OperationQueryService.java' | sed 's|.*/web/||'
|
|
mvc/operation/OperationHttpController.java:6:import dev.caskeleton.adapter.inbound.web.operationasync.OperationQueryService;
|
|
mvc/operation/OperationHttpController.java:45: private final OperationQueryService operations;
|
|
mvc/operation/OperationHttpController.java:52: public OperationHttpController(OperationQueryService operations) {
|
|
webflux/operation/ReactiveOperationHttpController.java:6:import dev.caskeleton.adapter.inbound.web.operationasync.OperationQueryService;
|
|
webflux/operation/ReactiveOperationHttpController.java:28: * {@link OperationQueryService}, which both call. What differs is only what has to: the store is
|
|
webflux/operation/ReactiveOperationHttpController.java:46: private final OperationQueryService operations;
|
|
webflux/operation/ReactiveOperationHttpController.java:56: OperationQueryService operations, Scheduler blockingScheduler) {
|
|
exit=0
|
|
|
|
=== 8.3 WebOperationCatalog 를 읽는 쪽 ===
|
|
$ grep -rn 'WebOperationCatalog' --include=*.java adapter/inbound/web/src/main app-bootstrap/src/main | grep -v 'operation/WebOperationCatalog.java\|InMemoryWebOperationCatalog.java' | sed 's|.*/web/||'
|
|
mvc/autoconfigure/WebMvcPlatformAutoConfiguration.java:17:import dev.caskeleton.adapter.inbound.web.operation.InMemoryWebOperationCatalog;
|
|
mvc/autoconfigure/WebMvcPlatformAutoConfiguration.java:18:import dev.caskeleton.adapter.inbound.web.operation.WebOperationCatalog;
|
|
mvc/autoconfigure/WebMvcPlatformAutoConfiguration.java:58: @ConditionalOnMissingBean(WebOperationCatalog.class)
|
|
mvc/autoconfigure/WebMvcPlatformAutoConfiguration.java:59: public InMemoryWebOperationCatalog webOperationCatalog() {
|
|
mvc/autoconfigure/WebMvcPlatformAutoConfiguration.java:60: return new InMemoryWebOperationCatalog();
|
|
webflux/autoconfigure/WebFluxPlatformAutoConfiguration.java:13:import dev.caskeleton.adapter.inbound.web.operation.InMemoryWebOperationCatalog;
|
|
webflux/autoconfigure/WebFluxPlatformAutoConfiguration.java:14:import dev.caskeleton.adapter.inbound.web.operation.WebOperationCatalog;
|
|
webflux/autoconfigure/WebFluxPlatformAutoConfiguration.java:76: @ConditionalOnMissingBean(WebOperationCatalog.class)
|
|
webflux/autoconfigure/WebFluxPlatformAutoConfiguration.java:77: public InMemoryWebOperationCatalog webOperationCatalog() {
|
|
webflux/autoconfigure/WebFluxPlatformAutoConfiguration.java:78: return new InMemoryWebOperationCatalog();
|
|
admin/route/WebRouteInventory.java:4:import dev.caskeleton.adapter.inbound.web.operation.WebOperationCatalog;
|
|
admin/route/WebRouteInventory.java:50: public void requireRegisteredOperations(WebOperationCatalog catalog) {
|
|
advanced/functional/FunctionalRoutePolicyValidator.java:3:import dev.caskeleton.adapter.inbound.web.operation.WebOperationCatalog;
|
|
advanced/functional/FunctionalRoutePolicyValidator.java:22: private final WebOperationCatalog catalog;
|
|
advanced/functional/FunctionalRoutePolicyValidator.java:24: public FunctionalRoutePolicyValidator(WebOperationCatalog catalog) {
|
|
advanced/functional/FunctionalRouteRegistry.java:3:import dev.caskeleton.adapter.inbound.web.operation.WebOperationCatalog;
|
|
advanced/functional/FunctionalRouteRegistry.java:25: public FunctionalRouteRegistry(WebOperationCatalog catalog) {
|
|
exit=0
|
|
|
|
=== 8.4 fingerprint 정규화 형식 ===
|
|
$ grep -n 'SEPARATOR\|canonical.append' adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/idempotency/SemanticRequestFingerprintFactory.java
|
|
36: private static final char SEPARATOR = '\u001f';
|
|
68: canonical.append(operation.value());
|
|
71: (name, value) -> canonical.append(SEPARATOR).append(name).append('=').append(value));
|
|
72: canonical.append(SEPARATOR).append(encoder.canonicalize(command));
|
|
76: (name, value) -> canonical.append(SEPARATOR).append(name).append(':').append(value));
|
|
exit=0
|
|
|
|
$ grep -rn 'ByteBuffer.allocate(Integer.BYTES)\|putInt(encoded.length)' --include=*.java adapter | wc -l
|
|
17
|
|
exit=0
|
|
|