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>
187 lines
13 KiB
Plaintext
187 lines
13 KiB
Plaintext
# evidence 264 — cross-scope-autoconfiguration-roots
|
|
# revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
# cwd: /shared/codebase/clean-architecture-backend-template/src/messaging
|
|
# command: bash /tmp/xscope.sh
|
|
# ---- raw output ----
|
|
==== A. registry: every leaf, files, runtime_memberships ====
|
|
leaves: 62
|
|
1 adapter-inbound-graphql files= 534 rt=['app-bootstrap']
|
|
2 adapter-inbound-grpc files= 18 rt=[]
|
|
3 adapter-inbound-web files= 638 rt=['app-bootstrap', 'sample-portfolio']
|
|
4 adapter-inbound-websocket files= 253 rt=[]
|
|
5 adapter-outbound-cache-redis files= 390 rt=['app-bootstrap']
|
|
6 adapter-outbound-fileserver files= 119 rt=['app-bootstrap']
|
|
7 adapter-outbound-httpclient files= 370 rt=['app-bootstrap']
|
|
8 adapter-outbound-identifier files= 10 rt=['app-bootstrap', 'sample-portfolio']
|
|
9 adapter-outbound-messaging files= 69 rt=['app-bootstrap']
|
|
10 adapter-outbound-notification files= 240 rt=['app-bootstrap']
|
|
11 adapter-outbound-objectstorage files= 206 rt=['sample-portfolio']
|
|
12 adapter-outbound-persistence-jpa files= 605 rt=['app-bootstrap', 'sample-portfolio']
|
|
13 adapter-outbound-persistence-mongo files= 497 rt=['app-bootstrap']
|
|
14 adapter-outbound-support files= 9 rt=['app-bootstrap']
|
|
15 app-bootstrap files= 455 rt=['app-bootstrap']
|
|
16 application-core files= 1025 rt=['app-bootstrap', 'sample-portfolio']
|
|
17 domain-core files= 13 rt=['app-bootstrap', 'sample-portfolio']
|
|
18 grpc-admin files= 19 rt=[]
|
|
19 grpc-advanced-bootstrap files= 13 rt=[]
|
|
20 grpc-advanced-compat files= 25 rt=[]
|
|
21 grpc-advanced-diagnostics files= 8 rt=[]
|
|
22 grpc-advanced-edition files= 11 rt=[]
|
|
23 grpc-advanced-resilience files= 23 rt=[]
|
|
24 grpc-advanced-streaming files= 20 rt=[]
|
|
25 grpc-client files= 19 rt=[]
|
|
26 grpc-codegen files= 16 rt=[]
|
|
27 grpc-core-api files= 41 rt=[]
|
|
28 grpc-discovery files= 11 rt=[]
|
|
29 grpc-observability files= 7 rt=[]
|
|
30 grpc-operation-ledger-jpa files= 7 rt=[]
|
|
31 grpc-policy files= 82 rt=[]
|
|
32 grpc-proto-contract files= 11 rt=[]
|
|
33 grpc-server files= 24 rt=[]
|
|
34 grpc-spring-boot-starter files= 8 rt=[]
|
|
35 grpc-testkit files= 36 rt=[]
|
|
36 messaging-admin-api files= 28 rt=['app-bootstrap']
|
|
37 messaging-admin-runtime files= 20 rt=['app-bootstrap']
|
|
38 messaging-claim-check files= 11 rt=['app-bootstrap']
|
|
39 messaging-cloudevents files= 6 rt=['app-bootstrap']
|
|
40 messaging-core-api files= 95 rt=['app-bootstrap']
|
|
41 messaging-inbox-jdbc-postgresql files= 13 rt=['app-bootstrap']
|
|
42 messaging-kafka files= 61 rt=['app-bootstrap']
|
|
43 messaging-kafka-share-experimental files= 7 rt=[]
|
|
44 messaging-nats-experimental files= 11 rt=[]
|
|
45 messaging-observability files= 17 rt=['app-bootstrap']
|
|
46 messaging-outbox-jdbc-postgresql files= 28 rt=['app-bootstrap']
|
|
47 messaging-policy files= 32 rt=['app-bootstrap']
|
|
48 messaging-pulsar-experimental files= 12 rt=[]
|
|
49 messaging-rabbit files= 33 rt=['app-bootstrap']
|
|
50 messaging-reliability-api files= 15 rt=['app-bootstrap']
|
|
51 messaging-runtime-core files= 12 rt=['app-bootstrap']
|
|
52 messaging-schema-api files= 15 rt=['app-bootstrap']
|
|
53 messaging-schema-avro files= 8 rt=[]
|
|
54 messaging-schema-json files= 6 rt=['app-bootstrap']
|
|
55 messaging-schema-protobuf files= 6 rt=[]
|
|
56 messaging-security files= 17 rt=['app-bootstrap']
|
|
57 messaging-spring-boot-starter files= 44 rt=['app-bootstrap']
|
|
58 messaging-spring-cloud-stream-bridge files= 10 rt=[]
|
|
59 messaging-testkit files= 23 rt=[]
|
|
60 messaging-transport-spi files= 19 rt=['app-bootstrap']
|
|
61 sample-portfolio files= 279 rt=['sample-portfolio']
|
|
62 shared-contract files= 87 rt=['app-bootstrap', 'sample-portfolio']
|
|
total tracked files under registered leaves: 6747
|
|
shipped leaves: 35 build-only leaves: 27
|
|
|
|
==== B. every AutoConfiguration.imports in the repo ====
|
|
-- src/adapter/inbound/graphql/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.adapter.inbound.graphql.autoconfigure.GraphQlRootAutoConfiguration
|
|
-- src/adapter/inbound/web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.adapter.inbound.web.mvc.autoconfigure.WebMvcPlatformAutoConfiguration
|
|
dev.caskeleton.adapter.inbound.web.webflux.autoconfigure.WebFluxPlatformAutoConfiguration
|
|
-- src/adapter/outbound/cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.adapter.outbound.cache.redis.sdk.config.RedisSdkAutoConfiguration
|
|
-- src/adapter/outbound/messaging/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.adapter.outbound.messaging.autoconfigure.MessagingBridgeRootAutoConfiguration
|
|
-- src/adapter/outbound/persistence-mongo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.adapter.outbound.mongo.MongoRootAutoConfiguration
|
|
-- src/app-bootstrap/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.bootstrap.autoconfigure.fileserver.FileserverPlatformAutoConfiguration
|
|
dev.caskeleton.bootstrap.autoconfigure.httpclient.HttpClientPlatformAutoConfiguration
|
|
dev.caskeleton.bootstrap.autoconfigure.persistencejpa.PersistenceJpaRootAutoConfiguration
|
|
dev.caskeleton.bootstrap.autoconfigure.messaging.DisabledMessagingSentinelAutoConfiguration
|
|
dev.caskeleton.bootstrap.notification.NotificationRootAutoConfiguration
|
|
dev.caskeleton.bootstrap.activation.AdapterActivationAutoConfiguration
|
|
-- src/grpc/grpc-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.grpc.boot.GrpcPlatformAutoConfiguration
|
|
-- src/messaging/messaging-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
dev.caskeleton.messaging.autoconfigure.MessagingPlatformRootAutoConfiguration
|
|
|
|
==== C. composition root scan boundary ====
|
|
pattern = CaSkeletonApplication.AUTO_CONFIGURED_PACKAGES)
|
|
})
|
|
@ConfigurationPropertiesScan(
|
|
basePackages = {
|
|
"dev.caskeleton.bootstrap.async",
|
|
"dev.caskeleton.bootstrap.autoconfigure.fileserver",
|
|
"dev.caskeleton.bootstrap.autoconfigure.httpclient",
|
|
"dev.caskeleton.bootstrap.concurrency",
|
|
"dev.caskeleton.bootstrap.idempotency",
|
|
"dev.caskeleton.bootstrap.outbox",
|
|
"dev.caskeleton.bootstrap.redis",
|
|
"dev.caskeleton.bootstrap.runtime",
|
|
"dev.caskeleton.bootstrap.security",
|
|
"dev.caskeleton.bootstrap.settings",
|
|
"dev.caskeleton.bootstrap.tracing",
|
|
"dev.caskeleton.adapter.inbound.grpc",
|
|
"dev.caskeleton.adapter.inbound.web",
|
|
"dev.caskeleton.adapter.inbound.websocket",
|
|
"dev.caskeleton.adapter.outbound.cache.redis",
|
|
"dev.caskeleton.adapter.outbound.fileserver",
|
|
"dev.caskeleton.adapter.outbound.objectstorage",
|
|
"dev.caskeleton.application",
|
|
"dev.caskeleton.domain",
|
|
"dev.caskeleton.shared"
|
|
})
|
|
public class CaSkeletonApplication {
|
|
|
|
/**
|
|
* Packages owned by an auto-configuration entry rather than by the component scan.
|
|
*
|
|
* <p>A package prefix rather than a class list: a new configuration added to an optional
|
|
* capability must not become active merely because nobody remembered to exclude it.
|
|
*
|
|
* <p>The Fileserver admin package is here for a different reason: those routes belong to the
|
|
* management context, and a component scan that also found them would publish the management
|
|
* plane on the public connector — the exposure the separate context exists to remove.
|
|
*
|
|
* <p>The web platform's error, budget and operation packages are here for a third reason. Their
|
|
* advices and controllers need beans that only exist when the corresponding platform
|
|
* auto-configuration is active, and a component scan finds them regardless — so an all-off or
|
|
* partially configured deployment failed to start on an unsatisfied dependency rather than simply
|
|
* not installing the control. Ownership by auto-configuration is what ties a control's presence
|
|
* to its dependency's.
|
|
*/
|
|
static final String AUTO_CONFIGURED_PACKAGES =
|
|
"dev\\.caskeleton\\.bootstrap\\.autoconfigure\\..*"
|
|
+ "|dev\\.caskeleton\\.bootstrap\\.notification\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.fileserver\\.admin\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.outbound\\.mongo\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.outbound\\.messaging\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.outbound\\.notification\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.outbound\\.persistence\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.graphql\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.mvc\\.error\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.mvc\\.budget\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.mvc\\.operation\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.webflux\\.error\\..*"
|
|
+ "|dev\\.caskeleton\\.adapter\\.inbound\\.web\\.webflux\\.operation\\..*"
|
|
+ "|dev\\.caskeleton\\.messaging\\..*";
|
|
|
|
==== D. MasterSwitch constants ====
|
|
21: PERSISTENCE_JPA("ca-skeleton.persistence-jpa.enabled", "APP_PERSISTENCE_JPA_ENABLED"),
|
|
23: PERSISTENCE_MONGO("ca-skeleton.persistence-mongo.enabled", "APP_PERSISTENCE_MONGO_ENABLED"),
|
|
25: MESSAGING("app.messaging.enabled", "APP_MESSAGING_ENABLED"),
|
|
27: NOTIFICATION_PLATFORM(
|
|
30: GRAPHQL("backend.graphql.enabled", "APP_GRAPHQL_ENABLED");
|
|
|
|
==== E. every @ConditionalOnProperty master gate in main sources ====
|
|
92 @ConditionalOnProperty(
|
|
18 @ConditionalOnProperty(prefix = "app.fileserver-platform", name = "enabled", havingValue = "true"
|
|
4 @ConditionalOnProperty(prefix = "app.object-storage", name = "enabled", havingValue = "true"
|
|
2 @ConditionalOnProperty(prefix = "ca-skeleton.objectstorage", name = "backend", havingValue = "s3"
|
|
2 @ConditionalOnProperty(prefix = "app.redis", name = "enabled", havingValue = "true"
|
|
2 @ConditionalOnProperty(prefix = "app.poster-image.api", name = "mode", havingValue = "legacy"
|
|
2 @ConditionalOnProperty(name = "app.messaging.broker", havingValue = "kafka"
|
|
1 @ConditionalOnProperty(prefix = MessagingSettings.PREFIX, name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "ca-skeleton.websocket", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "ca-skeleton.outbox", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "backend.web.budgets", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "backend.graphql", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "app.poster-image.api", name = "mode", havingValue = "publication"
|
|
1 @ConditionalOnProperty(prefix = "app.messaging.admin", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "app.messaging", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "app.jpa-platform", name = "addons-enabled", matchIfMissing = true
|
|
1 @ConditionalOnProperty(prefix = "app.fileserver", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(prefix = "app.file-export", name = "enabled", havingValue = "true"
|
|
1 @ConditionalOnProperty(name = "spring.mail.host"
|
|
1 @ConditionalOnProperty(name = "ca-skeleton.capabilities.lease.provider", havingValue = "redis"
|
|
1 @ConditionalOnProperty(app.messaging.broker=<id>
|