This file is a reference catalogue of every technology the backend (iris-service) actually uses,
plus a handful of well-known alternatives we intentionally rejected. Each entry is structured with
three fields so it can be skimmed quickly:
- What it is — a one-sentence plain-language definition.
- Usage here — how this repo concretely uses it, with file paths when relevant.
- Why it's pertinent — why this tech was picked over alternatives, or why the problem it solves matters to the project.
- Pairs with (optional, only when relevant) — the other entries in this glossary
whose function is genuinely intertwined with this one. Format:
[Other tech](#anchor) — one-line description of the coupling. Skip the field entirely when the tech is self-contained — empty "Pairs with: none" lines are noise.
Voice: entries are written in factual passive voice ("X has been rejected because…", "Y was preferred over Z because…"), not first-person ("we picked", "we did NOT pick"). The glossary is a reference document, not a personal log.
Concreteness rule for "Why it's pertinent": avoid generic praise ("battle-tested", "industry standard", "the right tool", "best in class", "production-grade"). Every "why" entry must answer at least ONE of:
- a measured benefit (numbers: latency, RSS, build time, CVE detection lag, % coverage),
- a named alternative that has been rejected with the specific reason (not "alternative X is worse" but "alternative X breaks Y on Z"),
- a concrete failure mode this tool prevents (a CVE class, an outage shape, a debugging dead-end),
- a link to the ADR that locked the decision in.
If none of those fit, the entry is filler — drop or rewrite.
Entries tagged (rejected) are tools that were evaluated and
deliberately not adopted. They exist in the glossary so the next
person (or the next Claude session) does not waste time re-evaluating
them.
Sibling catalogue for the Angular UI: https://gitlab.com/iris-7/iris-ui/-/blob/main/docs/technologies.md.
- Languages and runtimes
- Core frameworks
- Persistence and data
- Caching
- Messaging and real-time
- Authentication and authorization
- Resilience and rate limiting
- Observability
- AI and inference
- Testing
- Quality analysis
- Documentation
- Build and dependencies
- Version control and commits
- CI/CD
- Local dev environment
- Containers and images
- Kubernetes and orchestration
- Supply chain security
- Cloud providers and platforms
- Networking
- Cross-reference
☕ Java 25
- What it is: The current long-term (LTS) release of the Java platform (GA September 2025).
- Usage here:
<java.version>25</java.version>inpom.xml; the default build profile targets class-file major version 69. Virtual threads, ScopedValue, pattern matching with guards (case X e when ...) and record patterns are used inorg.iris.*. - Why it's pertinent: Virtual threads let the app serve thousands of concurrent HTTP + Kafka consumers with a small thread pool — a perfect fit for a Spring Boot service that spends most of its time waiting on DB, Kafka, Redis and HTTP. Running on LTS avoids the feature-release treadmill.
☕ Java 21 (compat)
- What it is: Previous LTS Java release.
- Usage here:
-Dcompatprofile (mvn verify -Dcompat) downgrades to 21 and copiessrc/main/java-overlays/pre-java25over main sources (ScopedValuerewritten asThreadLocal). Also used by thecompat-sb4-java21andcompat-sb3-java21CI jobs. - Why it's pertinent: Some customers still run on Java 21. Keeping a green build on 21 proves the codebase doesn't accidentally depend on Java 25-only syntax outside the overlay.
☕ Java 17 (compat)
- What it is: Older LTS Java release still widely deployed.
- Usage here:
-Djava17activation property triggers an additional overlaysrc/main/java-overlays/java17that rewritesswitchpattern matching asif/else. - Why it's pertinent: Documented baseline for conservative enterprise environments. CI runs
compat-sb4-java17andcompat-sb3-java17as manual jobs so we catch regressions before they reach a customer running Java 17.
☕ Java 26 (forward-looking)
- What it is: Next short-term Java release (GA March 2026).
- Usage here:
java26Maven profile;mvn verify -Djava26runs the full build on a Java 26 JDK when available. - Why it's pertinent: Early-warning pipeline — catches byte-code regressions in SpotBugs/ASM before Java 26 becomes the new LTS baseline.
- What it is: Alternative JVM implementation with an ahead-of-time (AOT) native-image compiler.
- Usage here: build/Dockerfile.native
stage 1 usesghcr.io/graalvm/native-image-community:25;mvn -Pnative native:compilerunsprocess-aotthennative:compile. CI jobbuild-native` produces the native image on the daily schedule or on demand. - Why it's pertinent: Startup drops from ~3 s to ~50 ms and RSS from ~250 MB to ~50 MB. Critical for serverless (Cloud Run) and GKE Autopilot where pod-level memory costs money.
- What it is: The intermediate instruction set emitted by
javac, versioned per Java release (69 = Java 25). - Usage here: Every library that parses bytecode (ASM, SpotBugs, PMD, ArchUnit) must support version 69 — documented in
pom.xmlcomments with upgrade pins. - Why it's pertinent: Version 69 is why we pin ASM 9.8, SpotBugs 4.8.6+, PMD 7.20+, Checkstyle 13.4+ — older versions crash when encountering Java 25 class files.
-
What it is: Free, production-grade builds of the OpenJDK project, maintained by the Adoptium working group at the Eclipse Foundation.
-
Usage here:
eclipse-temurin:25-jdkfor the builder stage andeclipse-temurin:25-jrefor the runtime stage inDockerfile. CI usesmaven:3.9.14-eclipse-temurin-25-nobleas the default image. -
Why it's pertinent: Permissively licensed (GPLv2 + Classpath Exception), TCK-certified, and the de-facto default in container images. Oracle JDK's licensing is hostile to long-running containers; Temurin just works.
-
Why Temurin vs the other free OpenJDK distributions: every alternative listed below also passes the Oracle TCK and ships free LTS builds — so the choice is governance + ecosystem fit, not compatibility:
Distribution Why it has been rejected Oracle JDK Free under NFTC for dev only; production use requires a paid Java SE Universal Subscription since Java 17. Hostile to a portfolio project that runs across CI, kind, GKE Autopilot, AKS, EKS without per-cluster billing. Amazon Corretto Excellent quality, but governance is AWS-controlled and the test matrix is biased toward EC2 / Graviton hardware. We deploy across GCP/AWS/Azure/Scaleway — picking a vendor's OpenJDK locks the project's "default" to that vendor's QA priorities. Azul Zulu (Community) TCK-certified and free, but the upgrade path nudges users toward paid Zulu Prime (their commercial Falcon JIT). Mixing CE in dev with Prime in prod creates a JIT-behaviour gap that is hard to debug. BellSoft Liberica Solid, ships JavaFX + musl/alpine variants this project does not need. Smaller community than Temurin — eclipse-temurin:*Docker pulls are roughly an order of magnitude higher, which means more eyes on CVEs and faster security patches in practice.Microsoft Build of OpenJDK New (2021), still catching up on CVE backporting cadence. Tied to Azure-priority testing. IBM Semeru / OpenJ9 Different VM (OpenJ9 instead of HotSpot) — better startup + RSS but worse compatibility with HotSpot-tuned libraries (Resilience4j, JFR-based profilers, Pyroscope). The Spring Boot 4 reference docs assume HotSpot; switching would mean re-validating every observability + perf assumption. GraalVM CE Used here for the native-image path ( build-nativejob, manual ▶) because Temurin doesn't shipnative-image. NOT used as the default JDK because GraalVM CE's HotSpot is identical-quality but the upgrade cadence trails Temurin by 1–2 weeks on critical CVE patches. The project keeps Temurin as the default and reaches for GraalVM CE only when AOT compilation is the goal.Net: Temurin is the only free OpenJDK distribution backed by a vendor-neutral foundation (Eclipse), with the largest container ecosystem, no commercial-tier upsell pressure, and the same JVM behaviour Spring Boot's reference docs were tested against. Switching distributions later is cheap (image tag change); switching governance models mid-project is not.
🧩 Lombok
- What it is: Annotation processor that generates boilerplate (getters, setters, builders, constructors) at compile time.
- Usage here:
providedscope dependency and explicitannotationProcessorPathsentry inmaven-compiler-plugin. Used on DTOs and entities inorg.iris.customer,org.iris.auth, etc. - Why it's pertinent: Eliminates ~30 % of boilerplate without adding runtime footprint. The explicit processor-path entry is required on Java 25 (implicit discovery was tightened).
- What it is: The canonical artifact repository for Java libraries (
repo1.maven.org/maven2), operated by Sonatype. - Usage here: SOLE
<repository>declared inpom.xml. Every production, test, and annotation-processor dependency resolves here. Spring AI 1.1.4 GA now lives on Central, so the Spring Milestones repo that used to host the 1.0.0-M6 milestone has been removed. - Why it's pertinent: Immutable, globally CDN-distributed, trust-anchored via Sonatype. Keeping it as the only repo (no
<pluginRepositories>for Spring Milestones, JBoss, etc.) shrinks the supply-chain attack surface AND speeds cold builds — fewer mirrors to probe, fewer auth headers to negotiate.
- What it is: Opinionated Spring-based application framework (GA late 2025, current major).
- Usage here: Parent POM
spring-boot-starter-parent:4.0.5. Default profile; everything underorg.iris.*assumes SB4 APIs. - Why it's pertinent: 4 concrete deltas vs Spring Boot 3 that motivated the upgrade in this repo:
- Java 25
@RestControllerworks without compiler flags (SB3 needs--enable-previewfor some pattern-matching cases, SB4 doesn't). - Unified
spring-boot-starter-opentelemetry— replaces the SB3 trio (micrometer-tracing-bridge-otel+opentelemetry-exporter-otlp+opentelemetry-spring-boot-starter) with a single starter. Cuts ~30 lines frompom.xmland removes 3 transitive-version mismatches that previously had to be pinned manually. - Structured logging on by default (
LoggingSystem.STRUCTURED) — Loki picks up trace IDs without our previous LogbackMDCfilter setup. RestTestClient— REST integration tests no longer needWebTestClient(which was reactive even in MVC apps) or the olderTestRestTemplate; one consistent API. Foundation rationale: dropping any of those forces re-introducing the SB3 workaround and an extracompat-profile.
- Java 25
🌱 Spring Boot 3 (compat)
- What it is: Previous Spring Boot major (Java 17+, still widely used).
- Usage here:
-Dsb3profile swaps the BOM to3.4.5, applies thejava-overlays/sb3overlay (replaces@GetMapping(version = ...)) and usesmicrometer-tracing-bridge-otelinstead of the SB4 OTEL starter. - Why it's pertinent: Back-compat target for customers that haven't migrated. The overlay mechanism means we never fork the codebase.
- What it is: Core Spring container (DI, AOP, MVC, transactions). Bundled by SB4.
- Usage here: Every
@Component,@Service,@Configuration,@Controller,@Transactionalinorg.iris.*. - Why it's pertinent: It is Spring Boot. No alternative is realistic for a Java-centric team.
- What it is: Servlet-based HTTP controller framework.
- Usage here: REST controllers under
org.iris.customer,org.iris.auth,org.iris.api. Pulled in viaspring-boot-starter-web. - Why it's pertinent — vs Spring WebFlux (the rejected alternative): WebFlux requires reactive types end-to-end (
Mono<T>/Flux<T>in every controller, repository, filter). Mixing the two paradigms in one app produces context-loss bugs that are nearly impossible to debug — aMonoconsumed twice silently emits nothing, and the stack trace points 8 lambdas deep. Spring MVC + virtual threads (SB4 wiresExecutors.newVirtualThreadPerTaskExecutor()by default since 4.0) gives the SAME concurrency benefit as WebFlux for I/O-bound workloads (DB / Kafka / Redis / HTTP — i.e. our entire workload) without forcing the reactive paradigm on@RestControllercode. Net: identical throughput on this project's workload, ~70 % less code complexity. - Pairs with: Java 25 (virtual threads make the MVC-vs-WebFlux choice irrelevant for I/O), Embedded Tomcat 11 (the servlet container behind it), Spring Security (filter chain runs first), springdoc-openapi (introspects MVC controllers to build /v3/api-docs).
- What it is: Reference Java servlet container, embedded into the Spring Boot JAR.
- Usage here: Default transitive dependency of
spring-boot-starter-web. Pinned to11.0.21via<tomcat.version>for CVE patches (CVE-2026-34483/486/487/500). - Why it's pertinent: Stable, virtual-thread compatible, and the Spring Boot team tests against it. Pinning forward catches CVEs faster than waiting for the next SB point release.
- What it is: Repository abstraction over JPA with derived-query method names.
- Usage here:
CustomerRepositoryand friends underorg.iris.customer. Pulled viaspring-boot-starter-data-jpa. - Why it's pertinent: Cuts repository boilerplate to one interface line per entity. Pairs naturally with Hibernate + HikariCP + Flyway.
- What it is: Reference JPA provider — Java-object-to-SQL mapping.
- Usage here: Transitive default under
spring-boot-starter-data-jpa.spring.jpa.hibernate.ddl-auto=validateenforces that entities match the Flyway-migrated schema at startup. - Why it's pertinent: The only widely-adopted JPA implementation; Spring Data JPA is written against its specifics.
ddl-auto=validatecatches entity-vs-schema drift at boot.
🗄️ HikariCP
- What it is: High-performance JDBC connection pool, Spring Boot's default.
- Usage here: Implicit —
spring-boot-starter-jdbc/jpabrings it in automatically. We override zero defaults — the autotunedmaximum-pool-size = max(10, processors × 2)matches our workload exactly. - Why it's pertinent vs alternatives: 2 concrete reasons the default is kept rather than switching to e.g. Tomcat JDBC, c3p0, or DBCP2:
- Sub-millisecond connection acquisition under contention — HikariCP's
ConcurrentBagdata structure avoids the synchronized-block contention that hurts c3p0 and DBCP2 above ~50 concurrent borrowers. Project load tests (bin/loadtest.sh) hit ~2k req/s sustained without acquisition latency showing up in p99. - Leak detection on by default (
leakDetectionThresholdconfigurable viaspring.datasource.hikari.leak-detection-threshold) — catches missingtry-with-resourcesblocks at runtime before they manifest as pool exhaustion in prod. Set to 30 s in dev and 60 s in CI.
- Sub-millisecond connection acquisition under contention — HikariCP's
- Pairs with: PostgreSQL 17 (the only DB driver this pool serves), Spring Data JPA + Spring Data JDBC (the two clients borrowing connections).
Storage / cache / broker stacks — Redis, Caffeine, Kafka — are documented as ONE entry each (the underlying service + the Spring integration library) in their domain sections below: see Caching for Redis + Caffeine and Messaging for Kafka. Keeping the "Spring side" co-located with the service avoids the previous two-entries-per-stack split where readers had to cross-reference to get the full picture.
- What it is: Bidirectional messaging over WebSocket with the STOMP sub-protocol.
- Usage here:
spring-boot-starter-websocket; real-time notification endpoint wired inorg.iris.messaging.WebSocketConfig. Frontend consumes it via SockJS. - Why it's pertinent: STOMP gives publish/subscribe semantics over a single TCP connection; simpler than SSE for bi-directional use cases.
- What it is: Authentication and authorization framework for Spring.
- Usage here:
spring-boot-starter-security+spring-boot-starter-oauth2-resource-server; JWT + OIDC configured inorg.iris.auth.SecurityConfig. - Why it's pertinent: Handles method-level authorization (
@PreAuthorize), JWT resource-server validation, CORS, CSRF policy, and the filter chain. Rewriting this is how applications get CVEs.
- What it is: HATEOAS link generation plus a browseable HAL explorer UI.
- Usage here:
spring-boot-starter-hateoasandspring-data-rest-hal-explorer— enables visual navigation of/actuator/and any HAL-capable endpoint. - Why it's pertinent: Makes the actuator tree discoverable for operators without them memorising paths.
- What it is: Production-ready operational endpoints (
/actuator/health,/actuator/prometheus,/actuator/info, etc.). - Usage here:
spring-boot-starter-actuator+ custom endpointorg.iris.observability.QualityReportEndpointexposed at/actuator/quality. - Why it's pertinent: Kubernetes liveness/readiness probes, Prometheus scraping, info banners — all consume actuator endpoints. Custom endpoints let us expose build-time quality artefacts (SpotBugs XML, JaCoCo CSV) without a separate service.
- What it is: Declarative validation via
@NotBlank,@Email,@Valid, backed by Hibernate Validator. - Usage here:
spring-boot-starter-validation; DTOs inorg.iris.customerand request bodies in controllers. - Why it's pertinent: Validation lives next to the field it protects (the DTO), not in the controller. Failures become 400s automatically via Spring MVC.
- What it is: Build-time ahead-of-time processing that bakes bean-definition metadata into the JAR, required for GraalVM native images.
- Usage here:
spring-boot-maven-pluginprocess-aotgoal in thenativeprofile. - Why it's pertinent: Without AOT, GraalVM can't see through Spring's reflection-based DI. AOT resolves all beans at build time so native-image has a closed world to compile.
- What it is: Open-source relational database.
- Usage here:
postgres:17image indocker-compose.yml; managed Cloud SQL instance indeploy/terraform/gcp/main.tf. Drive viaorg.postgresql:postgresqlJDBC driver (runtime scope). - Why it's pertinent: Mature ACID DB with JSONB, partitioning, and logical replication. Spring Boot autoconfigures around it seamlessly. Version 17 is the current major.
🗄️ Flyway
- What it is: Versioned SQL migration tool.
- Usage here:
spring-boot-starter-flyway+flyway-database-postgresql(Flyway 10+ unbundled drivers). Migrations insrc/main/resources/db/migration/V*__*.sql, includingV2__create_shedlock_table.sql. - Why it's pertinent: Schema evolution must be deterministic and peer-reviewable. Flyway file naming enforces uniqueness (never two
V1__), and Spring runs it at startup before Hibernate'svalidate.
- What it is: Standard Java DB APIs — raw JDBC and the higher-level JPA specification (Jakarta since Spring 6).
- Usage here: JPA via Spring Data, raw JDBC via
JdbcTemplateinorg.iris.resilience.ShedLockConfig. - Why it's pertinent: JPA for entity CRUD, JDBC for occasional surgical queries (ShedLock, health checks) — choosing the right tool per call.
- What it is: Managed PostgreSQL/MySQL service on Google Cloud.
- Usage here: Provisioned in
deploy/terraform/gcp/main.tf; the GKE overlay injects a Cloud SQL Auth Proxy sidecar so the app connects via a local socket. - Why it's pertinent: Point-in-time recovery, automated HA failover, and IAM-authenticated connections — none of which we would reliably rebuild on self-hosted Postgres.
- What it is: Google-provided sidecar that terminates a TLS tunnel to Cloud SQL using Workload Identity, exposing a local unix/TCP socket.
- Usage here: Injected as a container in the GKE overlay (
deploy/kubernetes/overlays/gke/). - Why it's pertinent: No DB password in transit, no VPC peering, no public IP — auth piggy-backs on K8s ServiceAccount → GCP IAM via Workload Identity. Best practice for Cloud SQL on GKE.
🔴 Redis 7 (server) + Spring Data Redis / Lettuce (client)
- What it is: Redis 7 — in-memory key-value store with rich data types (LIST, HASH, SET, SORTED SET, STREAMS). Spring Data Redis is the Spring abstraction; Lettuce is the non-blocking netty-based driver bundled by default.
- Usage here:
- Server:
redis:7in Compose; in Kubernetes a Deployment indeploy/kubernetes/base/stateful/redis.yaml. App connects viaSPRING_DATA_REDIS_HOST=redis. - Client:
spring-boot-starter-data-redis+StringRedisTemplateinorg.iris.customer.RecentCustomerBuffer(LPUSH + LTRIM + LRANGE ring of last-10 customers), JWT blacklist (org.iris.auth.JwtTokenProvider), idempotency keys, rate-limit buckets.
- Server:
- Why it's pertinent: Sub-ms latency, cross-pod state, and the right data types for our use cases (ring buffer via LIST, distinct keys via SET). Lettuce is safe to share across virtual threads — no per-call thread blocking. Cross-pod scope is what makes Redis the right answer for "logout token X across all replicas" — Caffeine (in-JVM) cannot.
- Pairs with: Caffeine (decision matrix: in-JVM-loss-OK vs cross-replica-coordinated), Bucket4j (rate-limit buckets stored here when running >1 replica), JWT strategy (blacklist for revoked access tokens), Memorystore for Redis (managed GCP equivalent).
- What it is: Google Cloud's managed Redis service.
- Usage here: Provisioned by Terraform as
google_redis_instance.cacheindeploy/terraform/gcp/main.tf(size configurable viaredis_tier/redis_memory_size_gb). The GKE overlay currently still inherits in-cluster Redis from the basestateful/overlay — the Memorystore host is plumbed through Terraform outputs but the Kustomize switchover is pending. - Why it's pertinent: Managed failover and patching once we cut over from the in-cluster Redis deployment.
- What it is: Official Redis Labs web UI for browsing keys, TTLs, memory.
- Usage here: Container in
docker-compose.yml(port 5540). Add connection host=redis port=6379. - Why it's pertinent: Visibility into cache eviction and key TTL issues during development.
- What it is: Lightweight Node-based Redis UI with live command stream.
- Usage here: Compose service (port 8082), auto-connects to the
redisservice. - Why it's pertinent: Lets us watch idempotency keys and rate-limit buckets mutate live. Smaller than RedisInsight, good complement for tail-the-log style debugging.
🗄️ Caffeine (in-JVM cache) + Spring Cache Abstraction
- What it is: Caffeine — high-performance in-process Java cache (the W-TinyLFU successor to Guava Cache, near-zero GC pressure). Spring Cache Abstraction is the
@Cacheable/@CacheEvictannotation framework with a pluggable backend; Caffeine is the recommended in-JVM backend. - Usage here:
spring-boot-starter-cache+com.github.ben-manes.caffeine:caffeinefor entity lookups (findById) — single-pod hot reads where the network round-trip to Redis would dominate the JDBC fetch. - Why it's pertinent — and why it does NOT overlap with Redis:
- Caffeine = ~µs latency, lost on pod restart, NOT shared across replicas. Right for hot reads tolerant to staleness on restart.
- Redis = ~1 ms latency, survives restart, shared across replicas. Right for state that must be coordinated (token blacklist, rate-limit buckets) or carry a TTL (idempotency keys).
- PostgreSQL = ~5–10 ms, durable, shared. Right for state that must outlive the cluster. Picking the right layer is a one-line decision: do you need cross-replica coordination → Redis; do you need durability → Postgres; otherwise Caffeine.
📨 Apache Kafka (KRaft) (broker) + Spring Kafka (client)
- What it is: Apache Kafka — distributed log / event-streaming platform; KRaft mode replaces ZooKeeper with an internal Raft quorum. Spring Kafka wraps the Apache Java client with listener containers,
KafkaTemplate, and request-reply patterns. - Usage here:
- Broker:
apache/kafka:4.0.0in Compose, single-broker/controller KRaft mode. In Kubernetes a StatefulSet indeploy/kubernetes/base/stateful/kafka.yaml. Topics auto-created on first publish. - Client:
spring-kafkadependency; producers / consumers /@KafkaListenerannotations live inorg.iris.messaging(KafkaConfig, listener classes). Used for fire-and-forget audit events AND request-reply enrichment with built-in correlation + timeout.
- Broker:
- Why it's pertinent: Async decoupling, replay, and consumer groups. KRaft eliminates the ZooKeeper operational tax — half the moving parts for small clusters. Spring Kafka removes the boilerplate of hand-rolling
KafkaConsumerpolling loops and integrates@KafkaListenerwith Micrometer observations out of the box.
- What it is: Web UI for topics, messages, consumer groups and ACLs.
- Usage here:
provectuslabs/kafka-ui:v0.7.2in Compose (port 9080). - Why it's pertinent: Much faster than
kafka-console-consumer.shscripts when you need to peek at topic content during development.
📨 Google Cloud Managed Kafka (rejected for now)
- What it is: GCP's managed Kafka offering.
- Usage here: Not used.
deploy/terraform/gcp/kafka.tfhas scaffolding but is commented out or disabled. - Why it's pertinent: Tracked as a future migration once sustained throughput justifies the per-GB/s pricing. For now, in-cluster Kafka on GKE is adequate and cheap.
- What it is: One-way server-push over HTTP, native browser
EventSource. - Usage here:
CustomerController.stream()exposesGET /api/customers/streamreturning aSseEmitter;SseEmitterRegistry(thread-safeCopyOnWriteArrayList) fans out customer-created / enriched events with a 5-minute keep-alive timeout. - Why it's pertinent: One-directional push fits our "customer feed" UI perfectly — simpler than WebSocket/STOMP, no library on the browser side (
new EventSource()), works through corporate proxies that break WebSocket.
Three identity-token flows in this project — pick the right one per use case:
- Built-in path —
Authorization: Bearer <JWT>issued by the app itself, signed with HS256 (HMAC, shared secret). Validated byJwtTokenProvider. No external dependency, fits the demo.- Keycloak / Auth0 path — JWT signed by an external IdP with RS256 (asymmetric). The app validates against the IdP's JWKS endpoint via
spring-boot-starter-oauth2-resource-server. Kicks in whenKEYCLOAK_URLorAUTH0_DOMAINis set.- CI → cloud path — GitLab CI gets a short-lived GCP access token via Workload Identity Federation (WIF) by exchanging its OIDC token for a GCP one. No long-lived service-account JSON in CI variables.
All three rely on OIDC as the wire format. JWT entries below are grouped together first; IdPs (Keycloak, Auth0) and CI federation (WIF) follow.
- What it is: Signed JSON claims, transmitted as
Authorization: Bearer <token>. - Usage here: Primary auth mechanism — validated by
JwtTokenProvider(org.iris.auth). Also accepted via Spring Security OAuth2 Resource Server for Keycloak/Auth0 tokens. - Why it's pertinent: Stateless auth means any pod can validate any request without shared session state — essential for horizontal scaling.
- Pairs with: JJWT (the Java library that signs and validates them), JWKS (public-key set used for RS256 validation), Spring Security OAuth2 Resource Server (the validating filter), Redis 7 (blacklist for revoked access tokens).
- What it is: JSON document listing the public keys a JWT issuer uses for signing. Each key has a
kid(key ID) so a token's header can point at the exact key that signed it. - Usage here: Fetched from Keycloak (
<KEYCLOAK_URL>/realms/<realm>/protocol/openid-connect/certs) or Auth0 (https://<AUTH0_DOMAIN>/.well-known/jwks.json) byspring-boot-starter-oauth2-resource-serveron first incoming RS256 token, then cached. - Why it's pertinent: Key rotation is transparent — the IdP publishes the new public key, clients refetch the JWKS and validate against it without code changes. Required for the RS256 path; the built-in HS256 path uses a static shared secret instead.
- Pairs with: JWT (what JWKS lets you validate), Spring Security OAuth2 Resource Server (the consumer of JWKS), Keycloak + Auth0 (the issuers).
- What it is: Leading Java JWT library by Les Hazlewood.
- Usage here: Three-artifact split —
jjwt-api(compile),jjwt-impl(runtime),jjwt-jackson(runtime) at version0.12.6. - Why it's pertinent: The three-JAR split keeps the compile-time API small and prevents leaking
Implclasses into consumer code. Used byJwtTokenProvider. - Pairs with: JWT (what it produces / parses), used by
JwtTokenProviderinorg.iris.authfor the built-in HS256 path only — the RS256 / JWKS path goes through Spring Security'snimbus-jose-jwtinstead.
- What it is: Spring Security module that validates JWT bearer tokens against a JWKS endpoint.
- Usage here:
spring-boot-starter-oauth2-resource-server. Activated whenKEYCLOAK_URL(orAUTH0_DOMAIN) is set. - Why it's pertinent: Lets the app accept Keycloak or Auth0 tokens without bespoke JWKS polling. Pulls in
nimbus-jose-jwttransitively. - Pairs with: JWT (what it validates), JWKS (where it pulls public keys from), Keycloak + Auth0 (the issuers it accepts), OIDC (the discovery protocol it uses to find JWKS endpoints).
🔐 Keycloak
- What it is: Self-hosted open-source OIDC / SAML identity provider.
- Usage here:
quay.io/keycloak/keycloak:26.2.5in Compose; realmcustomer-serviceimported frominfra/keycloak/realm-dev.json. Two M2M clients —api-gatewayandmonitoring-service. - Why it's pertinent: Demonstrates an OSS alternative to Auth0 for customers with on-prem requirements. Integration-tested via
testcontainers-keycloak. - Pairs with: JWKS (publishes its signing keys here), OIDC (its wire protocol), Spring Security OAuth2 Resource Server (validates its tokens).
🔐 Auth0
- What it is: Okta-owned SaaS identity provider.
- Usage here: Production path;
docs/auth0-action-roles.jsshows the post-login role-mapping action. - Why it's pertinent: Zero-ops MFA, social login, and passwordless for the UI. Backend only needs to validate the issuer/JWKS.
- Pairs with: JWKS, OIDC, Spring Security OAuth2 Resource Server — same trio as Keycloak. Auth0 and Keycloak are interchangeable from the backend's point of view since both speak standard OIDC.
- What it is: Identity layer on top of OAuth 2.0.
- Usage here: Both Keycloak and Auth0 speak OIDC — the same resource-server config works for both. WIF (below) also uses OIDC for the GitLab-CI → GCP token exchange.
- Why it's pertinent: Standardising on OIDC means IdPs swap without code changes, AND the same protocol covers both human-identity flows (Keycloak/Auth0) and machine-identity flows (WIF) — one mental model, two use cases.
- Pairs with: JWT (the token format it issues), Keycloak + Auth0 + WIF (the three OIDC issuers in this project).
- What it is: Google Cloud mechanism that trades an external OIDC token (e.g. GitLab CI JWT) for a short-lived GCP access token.
- Usage here: Used by
terraform-*anddeploy:gke/deploy:cloud-runjobs — see theexternal_accountcredential block in.gitlab-ci.yml. - Why it's pertinent: No long-lived service-account JSON keys in CI variables — the most common credential leak vector for GCP.
- Pairs with: OIDC (the protocol it federates over), Sigstore Fulcio (uses the SAME idea — exchange OIDC token for a short-lived credential, here a signing cert instead of an access token).
- What it is: Kubectl auth plugin that exchanges a gcloud access token for a K8s API server credential.
- Usage here: Installed by
deploy:gkeviagcloud components install gke-gcloud-auth-plugin. - Why it's pertinent: Required since K8s 1.26 — the deprecated in-tree GCP auth was removed.
- What it is: Fault-tolerance library providing
@CircuitBreaker,@Retry,@RateLimiter,@TimeLimiter,@Bulkhead. - Usage here:
io.github.resilience4j:resilience4j-spring-boot3:2.3.0. Applied to outbound HTTP inorg.iris.integrationandorg.iris.resilience. - Why it's pertinent: Dependency-free (no Hystrix-style archaius), Spring-Boot-native, and composable via annotations. Hystrix is end-of-life.
🛟 Bucket4j
- What it is: Token-bucket rate-limiting library.
- Usage here:
com.bucket4j:bucket4j-core:8.10.1; per-IP 100 req/min filter — threshold chosen to match Cloudflare's default DDoS floor. - Why it's pertinent: In-process (no extra infra for basic limits); backing store can be swapped to Redis later if we need cross-pod limits.
⏰ ShedLock
- What it is: Distributed lock over a shared data store, used to de-duplicate scheduled jobs in a cluster.
- Usage here: Two artefacts —
shedlock-spring(annotation + AOP) andshedlock-provider-jdbc-template(JDBC backend). Backing table created byV2__create_shedlock_table.sql. Wired inorg.iris.resilience.ShedLockConfig. - Why it's pertinent: Without this, a
@Scheduledtask running on 3 pods would fire 3× per tick.
- What it is: Standard availability patterns from the Release It! playbook.
- Usage here: Encoded via Resilience4j annotations on external calls (Auth0 JWKS refresh, etc.).
- Why it's pertinent: Prevents a flaky downstream from taking down the entire service thread-pool.
- What it is: Vendor-neutral metrics facade for the JVM (like SLF4J for metrics).
- Usage here: Bundled via
spring-boot-starter-actuator; custom meters inorg.iris.observability. - Why it's pertinent: Switching the metrics backend (Prometheus, Datadog, CloudWatch) is a config change, not a code rewrite.
- What it is: Micrometer bridge that exposes metrics in the Prometheus text format.
- Usage here:
io.micrometer:micrometer-registry-prometheus; served at/actuator/prometheusand scraped by the OTel Collector in LGTM every 15 s. - Why it's pertinent: Prometheus format is the de-facto lingua franca — every dashboard tool understands it.
- What it is: Auto-configures Micrometer Observation around every JDBC
DataSourcecall. - Usage here:
net.ttddyy.observation:datasource-micrometer-spring-boot:2.2.1— produces JDBC query-count and latency histograms + slow-query alerts. - Why it's pertinent: Turns the DB into a first-class observable — queries that don't go through Spring Data still show up in Grafana.
- What it is: CNCF-standardised telemetry API + SDK for traces, metrics and logs.
- Usage here: SB4 pulls the unified
spring-boot-starter-opentelemetry. The SB3 profile falls back tomicrometer-tracing-bridge-otel+opentelemetry-exporter-otlp. - Why it's pertinent: Vendor-agnostic wire format (OTLP) means we can redirect traces from LGTM locally to Grafana Cloud or Datadog in production via config only.
- What it is: Wire protocol for telemetry, over gRPC (4317) or HTTP (4318).
- Usage here: App pushes to LGTM port 4318; production pushes to Grafana Cloud direct OTLP endpoint with auth via
OTEL_EXPORTER_OTLP_AUTH. - Why it's pertinent: Single export pipeline for traces + logs + metrics — replaces bespoke Zipkin / Jaeger / Elastic APM agents.
- What it is: Logback appender that emits log events as OTLP records.
- Usage here:
io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:2.16.0-alpha; installed byorg.iris.observability.OtelLogbackInstallerat runtime. - Why it's pertinent: Logs flow through the same OTLP pipeline as traces, so
trace_idcorrelation works end-to-end in Grafana.
- What it is: Single container bundling Loki, Grafana, Tempo, Mimir and Pyroscope for development.
- Usage here:
grafana/otel-lgtm:0.22.1indeploy/compose/observability.yml; OTel Collector inside the container scrapes/actuator/prometheusevery 15 s and ingests OTLP traces/logs. - Why it's pertinent: One container gives the full Grafana observability experience for local dev. Production uses Grafana Cloud (same stack, hosted).
📡 Loki
- What it is: Grafana Labs log aggregation system, indexed by labels not content.
- Usage here: Logs ingested via OTLP (ENABLE_LOGS_ALL=true). Query via
{trace_id="..."}to correlate with a trace. - Why it's pertinent: Label index is cheap to operate vs Elasticsearch full-text — fine for structured logs.
📡 Tempo
- What it is: Grafana Labs distributed tracing backend.
- Usage here: Traces arrive via OTLP 4318; Grafana datasource provisioning tweaked to correlate
trace_idwith Loki labels. - Why it's pertinent: Cost-effective trace store; links out to Loki for logs and Mimir for metrics from any trace view.
📡 Mimir
- What it is: Grafana Labs horizontally-scalable Prometheus storage, Prometheus-compatible API.
- Usage here: Replaces standalone Prometheus in the LGTM container (port 9091→9090 remap).
- Why it's pertinent: Multi-tenant, easier to scale than self-hosted Prometheus + Thanos.
📡 Grafana
- What it is: Dashboard and visualisation front-end for metrics, logs, traces and profiles.
- Usage here: Part of LGTM; dashboards provisioned from
infra/observability/grafana/dashboards-lgtm/. Plugins installed: Infinity (REST calls), Dynamic Text (HTML panels), Button (trigger actions). - Why it's pertinent: Single pane of glass across metrics + logs + traces + profiles.
- What it is: Hosted LGTM with free tier generous enough for dev/demo.
- Usage here: Production OTLP target; creds injected via
GRAFANA_OTLP_AUTHsecret (base64 ofinstanceId:apiToken). - Why it's pertinent: Zero-ops observability; free tier is enough for a service this size.
- What it is: Continuous CPU + memory profiler (part of Grafana stack).
- Usage here:
io.pyroscope:agent:2.1.2embedded SDK; pushes JFR profiles every 10 s. Wired inorg.iris.observability.PyroscopeConfig. - Why it's pertinent: Production profiling without a JVM
-javaagentflag — good for finding hot methods and allocation sites we'd otherwise never spot.
- What it is: OpenJDK-native low-overhead profiling format.
- Usage here: Used by the Pyroscope agent to collect CPU/alloc/wall samples.
- Why it's pertinent: ~1 % overhead for always-on profiling — perfect for continuous profiling.
- What it is: Spring's abstraction for LLM providers (
ChatClient,EmbeddingClient). - Usage here:
spring-ai-starter-model-ollama:1.1.4(GA). Used to generate customer bios inorg.iris.customer.BioService. - Why it's pertinent: Lets us swap Ollama for OpenAI/Anthropic/Bedrock by config. The empty compat-shim classes under
src/main/java/org/springframework/boot/autoconfigure/are still required — even 1.1.4 GA references the Spring Boot 3RestClientAutoConfiguration/WebClientAutoConfigurationpackages in@AutoConfiguration(after=...), resolved at annotation-processing time.
🤖 Ollama
- What it is: Self-hosted LLM runtime (llama.cpp + model loader).
- Usage here:
ollama/ollama:0.9.0in Compose, modelllama3.2pulled on first start (~2 GB). - Why it's pertinent: Free local inference means demos work without an OpenAI key. Model cache persisted via
ollama_datavolume.
🤖 llama3.2
- What it is: Meta's open-weights 3B-class chat model.
- Usage here: Default model the Compose
ollamaservice pulls at startup. - Why it's pertinent: Small enough to run on a laptop, good enough for bio-generation demos. Swap models by changing
ollama pull ....
- What it is: Current-generation Java test framework.
- Usage here: Pulled via
spring-boot-starter-test; unit tests end in*Test.java, integration tests in*ITest.java. - Why it's pertinent: Replaces JUnit 4 — cleaner lifecycle hooks, parameterised tests, extension model (used by ArchUnit, Testcontainers, Spring).
🧪 AssertJ
- What it is: Fluent assertion library (
assertThat(x).isEqualTo(y).isNotNull()). - Usage here: Default assertion library, bundled by
spring-boot-starter-test. - Why it's pertinent: Readable failure messages and rich collection/object matchers. Replaces Hamcrest's verbose style.
🧪 Mockito
- What it is: Mock/spy/verify library for JVM objects.
- Usage here:
spring-boot-starter-testbundle. Sonar job passes-XX:+EnableDynamicAgentLoadingso Mockito's Byte Buddy agent can self-attach on Java 25. - Why it's pertinent: Ubiquitous — every Java dev has the API memorised. The dynamic-agent flag is a Java 25 gotcha documented in the Sonar job.
- What it is: Java library that spins up real Docker services (Postgres, Kafka, Redis) for integration tests.
- Usage here:
testcontainers:junit-jupiter,testcontainers:postgresql,testcontainers:kafka(pinned to1.21.4so kafka + core stay aligned). Plusspring-boot-testcontainerswith@ServiceConnection. - Why it's pertinent: ITs run against the SAME Postgres 17 and Kafka versions as prod, not an in-memory impostor. Catches driver-level and schema-level bugs H2 never would.
- What it is: Community Testcontainers module for Keycloak.
- Usage here:
com.github.dasniko:testcontainers-keycloak:3.5.0; used by auth ITs that exercise the OIDC flow. - Why it's pertinent: Full OIDC integration test without a mocked IDP.
- What it is: Spring Kafka's in-JVM Kafka broker for unit tests.
- Usage here:
spring-kafka-test. - Why it's pertinent: Faster than a container for message-format unit tests; we still use the Kafka Testcontainer for full integration flows.
🧪 ArchUnit
- What it is: Library that asserts architectural rules (layering, dependency direction) in unit tests.
- Usage here:
archunit-junit5:1.4.0;ArchitectureTestin tests — currently@DisabledIfSystemPropertyon Java 25 pending ASM-over-ArchUnit fix. - Why it's pertinent: Catches "controller imports repository directly" style drift automatically. Disabled on Java 25 because its hard-coded bytecode check chokes on class-file version 69.
- What it is: Spring Security helpers for MVC tests (
with(user(...)),csrf()). - Usage here:
spring-security-testdependency. - Why it's pertinent: Avoids generating real JWTs in tests — you can impersonate a principal inline.
- What it is: SB4-only MockMvc DSL replacement.
- Usage here: Declared in the
sb4profile; used inCustomerRestClientITest(excluded from the SB3 test overlay). - Why it's pertinent: The RestTestClient API is fluent, reactive-style, and plays well with virtual threads.
🧪 Pitest
- What it is: Mutation testing tool — re-runs tests against tiny code mutations to measure how many are killed.
- Usage here:
pitest-maven:1.23.0withpitest-junit5-plugin:1.2.3. Skipped by default; runs on-Preport. Output intarget/pit-reports/and packaged underMETA-INF/build-reports/. - Why it's pertinent: Raw line coverage is a weak signal. Pitest tells us whether the tests actually assert anything meaningful. Kept off the default build because it takes minutes.
🧪 JaCoCo
- What it is: Bytecode-instrumentation code-coverage tool.
- Usage here:
jacoco-maven-plugin:0.8.14with six executions (prepare-agent,prepare-agent-integration,report,report-integration,merge,check). Gate at 70 % merged instruction coverage. - Why it's pertinent: Merged unit + IT coverage is the only accurate figure; unit-only would under-report because most real behaviour lives in
@SpringBootTestpaths.
- What it is: Maven plugins for unit tests (surefire) and integration tests (failsafe).
- Usage here: Surefire runs
*Testintestphase; failsafe runs*ITestinintegration-testand keeps going on failure so reports are always produced. - Why it's pertinent: Convention-based split lets Docker-needing tests run only where Docker is available. Failsafe's tolerant mode guarantees JaCoCo data gets written.
- What it is: Source-level style enforcer using configurable rule XML.
- Usage here:
maven-checkstyle-plugin:3.6.0with Checkstyle10.26.1(needed for Java 21case X when ...). Usesgoogle_checks.xml. Skipped by default; enabled by-Preport-static. - Why it's pertinent: Catches style drift (imports, naming) before it becomes an argument in code review. Pinned version solves
NoViableAltExceptionon pattern guards.
🧹 PMD
- What it is: Source-level static analyser for code smells, dead code, complexity.
- Usage here:
maven-pmd-plugin:3.26.0with PMD core7.20.0(force-override to avoidStackOverflowErrorin 7.17).targetJdk=21; runs on-Preport-static -Dcompat. Output intarget/pmd.xml. - Why it's pertinent: Good complement to SpotBugs — PMD operates on source AST, SpotBugs on bytecode. Different bugs surface in each. Version pin is critical; without it 7.17 blows the stack on Java 25 generics.
🧹 SpotBugs
- What it is: Bytecode-level static analyser (successor of FindBugs).
- Usage here:
spotbugs-maven-plugin:4.8.6.4with ASM9.8override for Java 25 bytecode. ThresholdMedium, fails on any Medium+ finding. Exclusions inconfig/spotbugs-exclude.xml. - Why it's pertinent: Finds null dereferences, resource leaks, broken
equals/hashCode, multi-threading bugs. ThresholdMediumpicks up real bugs without drowning in stylistic noise.
- What it is: SpotBugs plugin with 130+ security-specific detectors (OWASP Top 10).
- Usage here:
com.h3xstream.findsecbugs:findsecbugs-plugin:1.13.0wired as a SpotBugs dependency. - Why it's pertinent: SAST with zero extra infra — just one more JAR on the SpotBugs classpath.
- What it is: SaaS Sonar — static analysis dashboard (free for public repos).
- Usage here:
sonar-maven-plugin:5.1.0.4751. Orgiris-7, project keyiris-7_iris-service. Reads merged JaCoCo XML from both unit + IT. Exclusions defined inpom.xml<sonar.exclusions>. - Why it's pertinent: Free tier gives us Quality Gate, security hotspots, and tech-debt tracking without self-hosting SonarQube.
- What it is: On-prem Sonar. Single-branch analysis only on Community.
- Usage here:
sonarqube:communityindocker-compose.yml, data insonarPostgres database created byinfra/postgres/init-sonar.sql. Elasticsearch needsnofileulimit raised. - Why it's pertinent: Local mirror of the SonarCloud analysis before pushing. Essential when iterating on a new ruleset offline.
🧹 Semgrep
- What it is: Pattern-based SAST — rules written in simple YAML.
- Usage here:
semgrep/semgrep:latestimage, rulesetsp/java,p/spring,p/owasp-top-ten,p/secrets. Output in GitLab SAST format for the Security Dashboard. - Why it's pertinent: Free public rulesets, no account/token, ~1-2 min analysis. Chosen over Qodana (requires JetBrains account even on free tier).
- What it is: SCA tool — scans Maven deps against the NVD for known CVEs.
- Usage here:
dependency-check-maven:12.1.1. Fails build on CVSS ≥ 9. NVD DB cached in.owasp-data/(shared CI cache across branches). Committed baseline JSON report undersrc/main/resources/META-INF/build-reports/. - Why it's pertinent: First-party tool is in-band (same Maven run), offline, and produces HTML + JSON that can be archived. CVSS-9 gate blocks only the most severe issues, so transitive notices do not wake the on-call.
🦊 GitLab SAST template (rejected-ish)
- What it is: GitLab's built-in Semgrep wrapper (
Security/SAST.gitlab-ci.yml). - Usage here: Included but
when: neverbecause the free-tier stub intentionally exits 1. Kept included so GitLab's Security Dashboard renders when/if we upgrade. - Why it's pertinent: Our own
semgrep:job does the real work; this include is a placeholder for the day we get GitLab Ultimate.
🦊 GitLab Dependency Scanning template (rejected-ish)
- What it is: GitLab's Gemnasium-based Maven CVE scanner.
- Usage here: Included but
when: never(same free-tier stub problem).owasp-dependency-checkis our effective substitute. - Why it's pertinent: Keeps the hook in place so we can flip to
when: on_successonce we upgrade.
- What it is: GitLab's wrapper around gitleaks.
- Usage here:
Security/Secret-Detection.gitlab-ci.ymlinclude + our ownsecret-scanjob runningzricethezav/gitleaks:v8.21.4with project-specific.gitleaks.toml. - Why it's pertinent: Two passes — GitLab's wrapper for the Security Dashboard, ours for the project-local allowlist and plain output.
- What it is: Maven goal reporting used/unused/undeclared dependencies.
- Usage here: Execution in
pom.xmlwritestarget/dependency-analysis.txtinto the JAR classpath, consumed byQualityReportEndpointat/actuator/quality. - Why it's pertinent: Flags stale
<dependency>declarations that nobody uses — dead-weight trimming.
- What it is: Maven goal dumping the full resolved dependency graph.
- Usage here: Writes
target/dependency-tree.txtfor inclusion at/actuator/quality. - Why it's pertinent: When CVE scanners flag a transitive JAR,
dependency-tree.txttells you exactly which of our direct deps pulled it in.
- What it is: Generates a
THIRD-PARTY.txtlisting every dependency with its SPDX license. - Usage here:
org.codehaus.mojo:license-maven-plugin:2.5.0, bound togenerate-resources, writesTHIRD-PARTY.txtintoMETA-INF/build-reports/. - Why it's pertinent: License compliance audits; flags GPL/AGPL incompatibilities immediately.
- What it is: The three plugins above, all packaged into the JAR under
META-INF/build-reports/. - Usage here: Served by
QualityReportEndpointat/actuator/qualityin a single JSON. - Why it's pertinent: Operators get build-time quality metadata (tests, coverage, dependencies, licenses, CVEs) from the running app without hitting CI.
- What it is: GitLab-specific JSON format (
gl-code-quality-report.json) consumed by the MR Code Quality widget. - Usage here:
code-qualityCI job converts SpotBugs + PMD + Checkstyle XML into GitLab's schema via inline Python. - Why it's pertinent: MR diffs get inline annotations on changed lines without extra plugins.
- What it is: Maven's HTML site generator (
mvn site) combining project info + plugin reports. - Usage here: Runs in the daily
generate-reportsCI job and pushes the result to thereports/branch. Includes Surefire, Failsafe, JaCoCo, SpotBugs, Javadoc, JXR (the SB4-incompatible ones — PMD/Checkstyle/OWASP — are omitted or patched in withmaven-antrun-plugin). - Why it's pertinent: One command, full report HTML, served by the
maven-sitenginx container athttp://localhost:8084.
📚 Javadoc
- What it is: Java's native API-reference generator.
- Usage here:
maven-javadoc-plugin:3.11.2withlinksource=true— each class page links to its hyperlinked source. - Why it's pertinent: Compodoc equivalent for the backend; browsable offline.
📚 JXR
- What it is: Cross-reference HTML source browser.
- Usage here:
maven-jxr-plugin:3.6.0. Provides clickable source used by PMD/Checkstyle error links. - Why it's pertinent: Turns PMD violations into "click the rule → see the offending line" navigation.
- What it is: Library that auto-generates an OpenAPI 3 spec from Spring MVC annotations and serves a Swagger UI.
- Usage here:
springdoc-openapi-starter-webmvc-ui:3.0.3. Spec at/v3/api-docs, UI at/swagger-ui.html. Version 3.0.3 is pinned to patch DOMPurify CVEs in bundled Swagger UI. - Why it's pertinent: Single source of truth for API contract — regenerated from code, not written by hand.
- What it is: The browser UI bundled by springdoc — lets you try API calls interactively.
- Usage here: Served by springdoc at
/swagger-ui.html. - Why it's pertinent: Onboarding tool for new API consumers; no Postman collection to keep in sync.
- What it is: Short Markdown files recording a single architectural choice and its rationale.
- Usage here:
docs/adr/— decisions like Kustomize-over-Helm, buildx-over-Kaniko, Semgrep-over-Qodana live here. - Why it's pertinent:
CLAUDE.mddesign pattern — anyone (human or LLM) reading the repo can reconstruct the rationale behind past decisions without crawling Git history.
- What it is: Java build tool using XML
pom.xmldeclarative configuration. - Usage here:
maven-wrapperpins Maven version in.mvn/wrapper. CI usesmaven:3.9.14. All lifecycle phases defined inpom.xml. - Why it's pertinent: Incumbent Java build tool; massive plugin ecosystem. Gradle was considered and rejected — Maven's declarative model is easier to reason about at team-scale.
- What it is: Script + descriptor that downloads a pinned Maven version on first run.
- Usage here:
mvnw/mvnw.cmdat repo root; Dockerfile builder stage uses./mvnw dependency:go-offlinethen./mvnw package. - Why it's pertinent: Any machine with a JDK can build — no global Maven install required. Pinning means every developer and CI runner uses the same Maven version.
- What it is: "Bill of Materials" pattern — a parent POM or imported BOM pins transitive versions.
- Usage here:
spring-boot-starter-parent:4.0.5provides the main BOM;<dependencyManagement>pins ASM + protobuf for security overrides. - Why it's pertinent: One place to audit versions. Renovate/Dependabot bumps land in one line instead of scattered
<version>s.
🤖 Renovate
- What it is: Automated dependency updater (GitLab-hosted instance).
- Usage here: Config in
renovate.json;renovate-lintCI job validates our config withrenovate-config-validator --strict. - Why it's pertinent: Proactive dep bumps reduce CVE exposure window. Lint catches config errors before the scheduled bot run.
🤖 Dependabot (rejected on GitLab)
- What it is: GitHub's dependency updater.
- Usage here: Not used — we're on GitLab.
- Why it's pertinent: Explicit non-choice; Renovate is the GitLab-native equivalent.
- What it is: Java compiler plugin for Maven.
- Usage here: Configured to
<release>25</release>with explicitannotationProcessorPathsfor Lombok. - Why it's pertinent: Explicit processor paths are mandatory on Java 25 (implicit discovery removed).
- What it is: Integration-test runner (tolerates test failures so reports are still written).
- Usage here: Runs
*ITestclasses inintegration-testphase. - Why it's pertinent: JaCoCo
report-integrationdepends on artifact output even on test failure — failsafe guarantees it.
- What it is: Unit-test runner.
- Usage here: Runs
*Testexcluding*ITest. We overridedefault-testexecution to replace deprecatedsystemPropertieswithsystemPropertyVariables(SUREFIRE-2190 workaround). - Why it's pertinent: Kept fast by excluding Docker-dependent tests; their runtime moves to
failsafe.
- What it is: Copies and filters resource files.
- Usage here: Eight executions in
pom.xmlcopy build artefacts (JaCoCo CSV, SpotBugs XML, Surefire XML, PMD/Checkstyle XML, OWASP JSON, Pitest XML, dependency-tree.txt, THIRD-PARTY.txt) intoMETA-INF/build-reports/for/actuator/quality. - Why it's pertinent: The JAR is self-describing — build-time quality data ships with the binary.
- What it is: Runs arbitrary Ant tasks inside Maven.
- Usage here: Three executions: (1) creates
.owasp-data/README.mdthe first time the NVD scan populates the cache; (2) copies the OWASP HTML intotarget/site/post-site (dependency-check-maven12.x can't generate via<reporting>directly); (3) merges source overlays (pre-java25,java17,sb3) intotarget/merged-sources/for compat builds. - Why it's pertinent: Surgical file-ops no Maven plugin covers natively. Chosen over
exec:execbecause Ant's<copy if:set=...>is conditional without shell plumbing.
- What it is: Spring Boot's Maven packaging plugin — produces the layered fat JAR.
- Usage here:
build-infogoal (exposes Git SHA + build time at/actuator/info);process-aotin thenativeprofile. - Why it's pertinent: Layered JARs enable Docker layer cache reuse; AOT is mandatory for GraalVM native image.
- What it is: GraalVM's plugin that runs
native-imageduring the Maven build. - Usage here: In the
nativeprofile withadd-reachability-metadatagoal. - Why it's pertinent: Registers reflection/serialisation/proxy hints so
native-imagecan build a closed-world binary.
- What it is: Dependency inspection plugin.
- Usage here:
treeandanalyze-onlyexecutions feed/actuator/quality. - Why it's pertinent: Free visibility into what's actually on the classpath at runtime.
🌿 Git
- What it is: Distributed version control.
- Usage here: Repo at
gitlab.com/iris-7/iris-service. Permanentdevbranch, auto-merge tomainon green pipeline. - Why it's pertinent: Fundamental. The
devbranch pattern is documented inCLAUDE.mdto prevent accidental deletion.
🪝 lefthook
- What it is: Fast Git hooks manager (Go binary, no Node required).
- Usage here:
.config/lefthook.yml(moved from repo root on 2026-04-20 per root-hygiene rule;.config/is an XDG-style auto-discovery path, no flag needed). Pre-commit: glab CI lint, hadolint, kubectl dry-run, terraform fmt+validate, env key parity, pom hardcoded-version scan, xmllint, gitleaks. Commit-msg: Conventional Commits regex. Pre-push: unit tests. - Why it's pertinent: Catches the errors that previously took 4+ CI iterations to find. Fast-fail locally saves pipeline quota.
- What it is: Commit-message spec:
type(scope)?: subject(feat, fix, docs, etc.). - Usage here: Enforced in the
commit-msghook; powersbin/ship/changelog.shcategorisation. - Why it's pertinent: Machine-parseable history →
feat→ ✨ Features,fix→ 🐛 Bug fixes,!→ 💥 Breaking in every new CHANGELOG entry.
- What it is: Node-based Conventional Commits linter.
- Usage here:
config/commitlint.config.mjsdocuments the rules; actual enforcement is the pure-bash regex in.config/lefthook.yml(avoids anode_modules/dependency for a Java project). - Why it's pertinent: Config file documents intent even without the runtime — tooling can be swapped to the Node impl at any time.
- What it is: Two shell scripts (~200 LOC total) that generate CHANGELOG entries from Conventional Commits and promote a
stable-v*tag to a GitLab Release. - Usage here: Run locally after a tag push —
bin/ship/changelog.shprepends the new entry, commit + tag,bin/ship/gitlab-release.sh <tag>creates the Release object viaglab. Seedocs/how-to/changelog-workflow.md. - Why it's pertinent: Replaced
googleapis/release-please2026-04-23 (that tool is GitHub-API-only → 401 Bad Credentials against GitLab PAT). The shell version adds zeronode_modules/to a Java project and burns zero CI runner time.
🦊 glab
- What it is: Official GitLab CLI.
- Usage here:
.config/lefthook.ymlpre-commit runsglab ci linton.gitlab-ci.ymlchanges. Used interactively to create/merge MRs. - Why it's pertinent: Locally validates CI YAML against GitLab's API — one round-trip vs "push and wait for the pipeline".
🔒 gitleaks
- What it is: Fast secrets scanner for Git history and staged changes.
- Usage here:
zricethezav/gitleaks:v8.21.4in thesecret-scanCI job;gitleaks protect --stagedin lefthook pre-commit. Allowlist in.gitleaks.toml. - Why it's pertinent: Double gate (pre-commit + CI) keeps AWS/GCP keys and JWTs out of the repo. Pinned version (never
:latest) for reproducibility.
- What it is: GitLab's pipeline engine — YAML config at
.gitlab-ci.yml, runners execute jobs. - Usage here: ~60 jobs across stages
lint,test,integration,package,compat,native,sonar,reports,infra,deploy. Two scheduled pipelines (native build + report gen). - Why it's pertinent: Branch-aware workflow rules (
changes:allowlist) skip docs-only commits. Primary CI since all repos live on GitLab.
- What it is: Self-hosted GitLab runner on an Apple Silicon mac.
- Usage here: Tagged
macbook-local; primary runner for docker-build, trivy, sbom:syft, grype:scan, dockle, cosign, secret-scan, renovate-lint, deploy:gke. - Why it's pertinent: Zero marginal cost (no GitLab SaaS minutes burned). arm64 host for native compiles;
buildx --platform linux/amd64handles GKE's amd64 target.
🦊 GitLab SaaS shared runners (rejected)
- What it is: GitLab-hosted Linux runners (amd64 and arm64).
- Usage here: Not tagged — we rely on the local runner for most jobs.
saas-linux-medium-amd64is explicitly avoided because of monthly minute quota. - Why it's pertinent: The global
CLAUDE.mdrule "use local runners, never rely on SaaS quota" is encoded here.
- What it is: Docker's multi-platform build frontend using BuildKit.
- Usage here:
docker-buildCI job —buildx build --platform linux/amd64cross-compiles on the arm64 macbook runner. - Why it's pertinent: Kaniko cannot cross-compile; using buildx + QEMU on the local runner gives us amd64 images for GKE without burning GitLab SaaS quota.
- What it is: User-mode CPU emulator enabling cross-arch container builds.
- Usage here:
docker run --privileged --rm tonistiigi/binfmt --install amd64beforebuildx build. - Why it's pertinent: Required once per runner to register the amd64 interpreter under binfmt_misc. Idempotent, so the
|| trueis safe.
🧱 Kaniko (rejected for main image)
- What it is: Google's containerised Docker image builder, no daemon or privileged mode required.
- Usage here: Only used for
build-native(GraalVM native image) where cross-compilation isn't needed. Rejected for the primary docker-build because it can't cross-compile (arm64 host → amd64 image fails on GKE). - Why it's pertinent: Attractive on paper, but "builds on whatever arch the host is" is a landmine when the target cluster is a different arch.
🐳 Docker-in-Docker / docker:dind (rejected)
- What it is: A privileged Docker daemon running inside a CI service container.
- Usage here: Deliberately not used. Integration tests use the host Docker socket (
/var/run/docker.sock) viaTESTCONTAINERS_HOST_OVERRIDE=host.docker.internal. - Why it's pertinent: The
.gitlab-ci.ymlcomment spells out the DinD failure mode — inner container ports bind on172.17.0.1inside the DinD container and are unreachable from the job container. Socket binding is the durable fix.
🧹 hadolint
- What it is: Dockerfile linter written in Haskell.
- Usage here: Standalone
hadolint/hadolint:latest-alpinecontainer in thelintstage.--failure-threshold errormatches the pre-commit hook. - Why it's pertinent: Catches DL3002 (non-root USER), DL3007 (avoid
:latest), DL3008 (pin apt versions) — all real bugs that would otherwise ship to production.
- What it is: See Quality analysis section.
- Usage here:
semgrepCI job on the daily report schedule; artefactgl-sast-report.jsonpopulates GitLab's SAST widget. - Why it's pertinent: Deep rules + GitLab integration — covered once above; listed here for completeness.
All three are used, not "either / or". They have non-overlapping
roles in the supply-chain pipeline — picking just one would let real
CVEs slip through. The chain runs on every main push and tag:
docker-build ─► IMAGE pushed to registry
│
├──► syft ──► bom.cdx.json + bom.spdx.json (the ingredient list)
│ │
│ ▼
│ grype:scan (Java-focused, DB: GitHub Advisory + NVD)
│
└──► trivy:scan (image-wide, DB: aquasec/trivy-db)
🔒 syft — SBOM generator
- What it does: Walks the built image and inventories every package: OS packages from APK/APT/RPM, Java JARs by Maven coordinates, native libraries.
- Usage here:
anchore/syft:v1.18.1in thesbom:syftjob. Emits bothbom.cdx.json(CycloneDX) ANDbom.spdx.json(SPDX) as artifacts retained 90 days. - Why it's pertinent: SBOMs are mandated by NTIA + the EU Cyber Resilience Act. Producing both formats covers every compliance framework. The CycloneDX file is also the INPUT to Grype below.
🔒 Trivy — image scanner
- What it does: Pulls the registry image and scans the LAYERS themselves: OS package CVEs from the base image (Ubuntu noble), the Java runtime (Temurin 25 JRE), embedded JARs detected via Trivy's own analyzer.
- Usage here:
aquasec/trivy:0.69.3(pinned, never:latest— the upstream rename of:latestbroke pipeline #474). Severity filter: HIGH + CRITICAL only. No DinD or privileged mode — it reads the registry directly viatrivy registry login. - Why it's pertinent: Catches things SBOM-based scanners miss — e.g. a CVE in libssl that's installed at the OS layer but isn't declared in any pom.xml.
🔒 Grype — SBOM scanner
- What it does: Reads the CycloneDX SBOM produced by syft and
cross-references each Java coordinate (
groupId:artifactId:version) against the GitHub Advisory Database + NVD. Different DB, different matcher than Trivy. - Usage here:
anchore/grype:v0.87.0-debug(the-debugvariant ships/bin/shwhich GitLab CI'ssh -cwrapper requires) ingrype:scan. Depends onsbom:syftfor the SBOM artifact. - Why it's pertinent — i.e. why Trivy alone is not enough: Trivy and Grype use different vulnerability databases and different matching logic. A real-world Java CVE (e.g. log4shell-shaped issues on niche transitive deps) sometimes lands in GitHub Advisory days before Trivy DB picks it up; sometimes vice versa. Running both is CHEAP (parallel jobs, ~30 s each) and the false-negative reduction is worth far more than the doubled CI minute. The two tools also flag different CVE IDs for the same vulnerability sometimes, which makes triage faster — you cross-reference both reports.
TL;DR if you only remember one thing: syft = "what's in the image"; Trivy = "is the image vulnerable" (OS + runtime focus); Grype = "are the Java libs vulnerable" (Maven coordinates focus). Three tools, three answers, one supply-chain story.
🔒 dockle
- What it is: Image-hygiene scanner (CIS Docker Benchmark, Dockerfile best practices).
- Usage here:
goodwithtech/dockle:v0.4.15in thedocklejob. Checks USER non-root, HEALTHCHECK, suspicious env vars.allow_failure: true— hygiene warnings, not blockers. - Why it's pertinent: Complements Trivy (CVEs) with hygiene (posture). Catches the "forgot
USERdirective" class of regression.
Image signing in this project is keyless — no long-lived signing key to rotate or leak. Two pieces work together:
🔒 Sigstore Fulcio — keyless cert authority
- What it is: Sigstore's code-signing certificate authority. Issues short-lived (~10-min) X.509 certs against an OIDC identity instead of long-lived signing keys.
- Usage here: The
cosign:signCI job presents GitLab'sSIGSTORE_ID_TOKEN(an OIDC token whose claims include the project path + branch + pipeline ID) to Fulcio. Fulcio validates the token signature, then mints a cert binding the OIDC identity to a freshly-generated keypair. - Why it's pertinent: Eliminates the "signing key on a CI runner" leak vector entirely. The cert lives 10 min — by the time a leak would matter, it has expired. Provenance is recoverable from Sigstore's transparency log (Rekor) for years afterwards.
- Pairs with: cosign (consumes the cert it issues), WIF (parallel pattern — exchange OIDC for short-lived credential), SLSA / provenance (foundation for L3 attestations).
🔒 cosign — signs and verifies
- What it is: Sigstore's tool for signing OCI artefacts using Fulcio-issued short-lived certs. Also writes the signature + cert to Sigstore's append-only Rekor transparency log so verification works even after the cert expires.
- Usage here:
gcr.io/projectsigstore/cosign:v2.5.0incosign:sign. Keyless mode using GitLab'sSIGSTORE_ID_TOKEN. Verification side runs indeploy:gkeviacosign verifywith the issuer + identity-regex pinned to the GitLab project. - Why it's pertinent: Signed images let downstream consumers verify the artefact actually came from this project's CI, not a tampered registry. Keyless = no long-lived signing key to rotate. Rekor log = independently auditable provenance.
- Pairs with: Sigstore Fulcio (where cosign gets its signing cert), syft + Trivy + Grype 3-tool sandwich (parallel supply-chain protection — Fulcio/cosign prove provenance, the scanners prove no known CVEs).
- What it is: Competing SBOM formats — CycloneDX from OWASP, SPDX from Linux Foundation.
- Usage here:
sbom:syftemits both; GitLab CycloneDX artifact report renders in the UI. - Why it's pertinent: Auditors may demand either — emit both and be done.
- What it is: Multi-container orchestration file format (v3).
- Usage here:
docker-compose.yml(infra: db, kafka, redis, ollama, keycloak, app, admin UIs, SonarQube, nginx report servers) +deploy/compose/observability.yml(LGTM stack) +deploy/compose/runner.yml(optional GitLab runner container). - Why it's pertinent: One command stands up the full dev stack. Keeps
docker compose up -das the entry point for every new developer.
- What it is: Web edition of DBeaver — same Eclipse-based ERD + SQL editor served as a self-hosted web app.
- Usage here:
dbeaver/cloudbeaver:26.0.2in Compose, port 8978. Admin password set on first visit, Postgres connection registered manually (hostdb, dbcustomer-service, userdemo). Volumecloudbeaver_datapersists the config. - Why it's pertinent: Replaced pgAdmin + pgweb. One tool instead of two, familiar DBeaver UI without forcing the desktop app on every dev. Redis/Kafka are still served by their own UIs (RedisInsight, Kafka UI) since CloudBeaver Community is SQL-only.
- What it is: Convention for per-developer environment variables.
- Usage here: Compose reads
.env;.config/lefthook.ymlenforces key parity between.envand.env.example. - Why it's pertinent: Every developer gets documented keys; nobody commits secrets. Parity check prevents drift.
- What it is: Project-local task runner (Bash script).
- Usage here:
./run.sh check,./run.sh sonar,./run.sh site, etc. Referenced from.config/lefthook.ymlpre-push (./run.sh check). - Why it's pertinent: Canonical entry points so humans and CI both invoke the same commands.
- What it is: OCI-compliant container runtime, the de-facto developer-facing tool.
- Usage here: Docker Desktop on the macbook runner.
docker:28image indocker-buildCI job. - Why it's pertinent: Still the lowest-friction way to build + ship images. buildx plugin bundled since 19.03.
- What it is: Industry spec for image format + distribution.
- Usage here:
org.opencontainers.image.*labels set inDockerfile+ CI build args. - Why it's pertinent: Standard labels are parsed by cosign, Trivy, GitLab registry UI without extra configuration.
- What it is: Adoptium's OpenJDK container images.
- Usage here:
eclipse-temurin:25-jdk(builder + layers stages),eclipse-temurin:25-jre(runtime stage). - Why it's pertinent: JRE-only runtime is ~180 MB smaller than JDK — no compiler in prod.
🐳 Google distroless (rejected)
- What it is: Minimal images with no shell, package manager, or utilities — just the runtime.
- Usage here: Not used.
Dockerfilecomment calls out thatdistroless-javaonly ships up to Java 21, which would fail on Java 25 bytecode. - Why it's pertinent: Great concept, blocked on Google publishing
distroless-java25. Re-evaluate when that lands.
- What it is: Spring Boot 3+ layered JAR layout:
dependencies/,spring-boot-loader/,snapshot-dependencies/,application/. - Usage here:
java -Djarmode=tools -jar app.jar extract --layers --launcherin the Dockerfile layers stage. Each layer becomes a separate Docker layer. - Why it's pertinent: Re-builds only the
application/layer on code change — dep layer stays cached.
- What it is: Spring Boot's launcher class that loads the exploded layered layout.
- Usage here:
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]. - Why it's pertinent: Regular
java -jar app.jardoesn't work after layer extraction; JarLauncher is the counterpart.
- What it is: Metadata on the image manifest (
revision,created,source, etc.). - Usage here: Static labels in
Dockerfile; dynamicrevisionandcreatedinjected bydocker buildx --label. - Why it's pertinent: Traceability from registry UI back to the exact CI commit. cosign signatures bind to the digest, labels let you resolve the image to a repo commit.
🌐 Nginx
- What it is: Widely used reverse proxy and static web server.
- Usage here:
nginx:1.27-alpinefor three Compose services —maven-site(8084),compodoc(8086), and thecors-proxyin the observability compose. - Why it's pertinent: Smallest usable static-file server; trivial to drop a
*.confnext to it.
☸️ Kubernetes
- What it is: Container orchestration system (Pods, Deployments, Services, StatefulSets).
- Usage here: Primary deployment target. Manifests in
deploy/kubernetes/base/and overlays indeploy/kubernetes/overlays/{gke,eks,aks,local}. - Why it's pertinent: Portable across GKE / EKS / AKS / k3s — the deploy jobs just swap the credential mechanism.
☸️ Kustomize
- What it is: K8s manifest overlay tool, built into kubectl (
kubectl kustomize). - Usage here:
deploy/kubernetes/overlays/<provider>/kustomization.yamlcomposes the base with provider-specific patches (Cloud SQL sidecar + cert-manager ingress for GKE, in-cluster Postgres for local/eks/aks). - Why it's pertinent: Pure-YAML, declarative, no templating DSL. Comments in
.kubectl-applyexplain why we pipekubectl kustomizethroughenvsubstfor variable expansion.
🚢 Helm (rejected)
- What it is: K8s templating + release manager.
- Usage here: Not used. Kustomize covers our customisation needs.
- Why it's pertinent: Helm's Go-template syntax is a poor fit for teams already comfortable with YAML; Kustomize "patches not templates" is simpler. Decision recorded in
docs/adr/.
🔧 envsubst
- What it is: GNU
gettexttool that substitutes$VARin stdin. - Usage here:
kubectl kustomize ... | envsubst | kubectl apply -f -in.kubectl-apply. Substitutes${IMAGE_REGISTRY},${IMAGE_TAG},${K8S_HOST},${CLOUD_SQL_INSTANCE}. - Why it's pertinent: Kustomize doesn't resolve env vars — envsubst fills the gap without a full templating engine.
☸️ kubectl
- What it is: Kubernetes CLI.
- Usage here:
bitnami/kubectl:latestfor k3s deploy; pinned versions for AKS/EKS; dynamic download for GKE (dl.k8s.io/release/stable.txt). Pre-commit hook runskubectl apply --dry-run=clienton changed manifests. - Why it's pertinent: Canonical K8s client;
--dry-run=clientis a zero-cost validation step.
- What it is: Google Cloud fully-managed Kubernetes — no node management.
- Usage here: Default production target (
deploy:gkeruns automatically on main). Uses Workload Identity + Cloud SQL Auth Proxy sidecar. - Why it's pertinent: Minimal ops — Google manages nodes, patching, autoscaling. Pays per-Pod resources rather than per-node.
- What it is: Managed K8s on AWS.
- Usage here: Manual deploy job
deploy:eks; imagealpine/k8s:1.30.2; overlaydeploy/kubernetes/overlays/eks/. - Why it's pertinent: Portability target; demonstrates the base manifests are cloud-agnostic.
- What it is: Managed K8s on Azure.
- Usage here: Manual deploy job
deploy:aksusingmcr.microsoft.com/azure-cli; overlaydeploy/kubernetes/overlays/aks/. - Why it's pertinent: Third portability target.
- What it is: Serverless managed-container platform — scales to zero.
- Usage here: Manual deploy job
deploy:cloud-run. Uses WIF for auth, reads DB password from Secret Manager. - Why it's pertinent: Best for demos and low-traffic environments; zero idle cost.
☁️ Fly.io
- What it is: Global PaaS with anycast routing.
- Usage here:
deploy:flymanual job usingflyio/flyctl:latest. - Why it's pertinent: Simplest possible prod deploy — good for side-project staging.
☸️ k3s
- What it is: Rancher's minimal K8s distribution.
- Usage here: Generic
deploy:k3sjob for any bare-metal/k3s/Hetzner/OVH cluster with kubeconfig built from CI vars. - Why it's pertinent: Covers the "I just want to run this on a Raspberry Pi" case.
- What it is: K8s Ingress controller backed by nginx.
- Usage here:
deploy/kubernetes/base/ingress.yamlroutes external traffic to the service; GKE overlay patches it with cert-manager + TLS. - Why it's pertinent: Most-deployed ingress controller; zero-surprise HTTP routing.
- What it is: K8s TLS automation (Let's Encrypt, in-house CAs).
- Usage here: GKE overlay annotates the ingress with
cert-manager.io/cluster-issuerfor automatic TLS provisioning. - Why it's pertinent: TLS rotation is automatic — no "cert expired on Friday night" pages.
☸️ StatefulSets
- What it is: K8s controller for stable-identity pods (Kafka brokers, Postgres primaries).
- Usage here:
deploy/kubernetes/base/stateful/kafka.yaml,redis.yaml,keycloak.yaml. - Why it's pertinent: Stable DNS names and persistent volume claims — prerequisite for stateful workloads.
- What it is: K8s autoscaler based on CPU/memory/custom metrics.
- Usage here:
deploy/kubernetes/base/backend/hpa.yaml. - Why it's pertinent: Autoscale under load; we sized
minReplicas/maxReplicasand thresholds with realistic production numbers (seeCLAUDE.md"justify every timeout" rule).
- What it is: K8s object bounding simultaneous voluntary pod evictions.
- Usage here:
deploy/kubernetes/base/backend/poddisruptionbudget.yaml. - Why it's pertinent: Prevents a node drain from taking down all replicas at once.
- What it is: K8s-native firewall rules for pod-to-pod traffic.
- Usage here:
deploy/kubernetes/base/networkpolicies.yaml. - Why it's pertinent: Default-deny posture — app only talks to what it actually needs.
- What it is: eBPF-based CNI; GKE Autopilot uses its fork called Dataplane V2.
- Usage here: Implicit on GKE Autopilot; enforces our NetworkPolicies.
- Why it's pertinent: Faster and more observable than iptables-based kube-proxy; powers the NetworkPolicy enforcement.
- What it is: K8s-triggered health checks that hit actuator endpoints.
- Usage here:
deploy/kubernetes/base/backend/deployment.yaml. StartupProbefailureThreshold=60 × periodSeconds=5 = 5 minbudget sized for cold-JVM + Flyway + node cold-start on GKE Autopilot. - Why it's pertinent: Default startup budgets (~30 s) kill our pods before Flyway finishes on a cold node; the explicit 5-minute budget is documented in
CLAUDE.md.
- What it is: K8s built-in admission controller enforcing the PodSecurity standard (replaces PodSecurityPolicy).
- Usage here: Namespace labels enforce
restrictedprofile indeploy/kubernetes/base/namespace.yaml. - Why it's pertinent: Declarative, no webhook to operate — replaces OPA/Gatekeeper for basic hardening.
- What it is: Deployment strategy (RollingUpdate by default).
- Usage here:
kubectl rollout status deployment/iris -n app --timeout=360sin.kubectl-apply. 6 min is deliberate — JVM warmup + Flyway + image pull on cold nodes exceed the default 180 s. - Why it's pertinent: Wrong timeout causes CI-false-positive failures that leave a half-rolled deployment.
- What it is: NIST's catalogue of CVEs with CVSS scores.
- Usage here: Primary feed for OWASP Dependency-Check (cached in
.owasp-data/). - Why it's pertinent: Canonical CVE source;
NVD_API_KEYspeeds up the first-run download.
- What it is: GitHub's CVE + language-specific advisory feed.
- Usage here: Consumed by Grype when scanning the SBOM.
- Why it's pertinent: Often lists Maven-coord CVEs before they hit NVD.
- What it is: 0-10 severity score assigned to each CVE.
- Usage here: OWASP Dep-Check
failBuildOnCVSS=9— only block on Critical. - Why it's pertinent: Tunable policy lets us track lower-severity issues without blocking deployment.
- What it is: Supply-chain Levels for Software Artifacts — graded provenance attestations.
- Usage here: Not formally attested yet; cosign signing + SBOM publication is the groundwork toward SLSA L3.
- Why it's pertinent: Progressive journey — signed images are the first step, provenance attestations are next.
- What it is: Google's public cloud.
- Usage here: Primary production target — GKE Autopilot, Cloud SQL, Artifact Registry, Cloud Run. Provisioned by
deploy/terraform/gcp/. - Why it's pertinent: Autopilot + Cloud SQL + Workload Identity is the lowest-ops combination we've found for a small team.
🏗️ Terraform
- What it is: HashiCorp's infrastructure-as-code tool.
- Usage here:
hashicorp/terraform:1.9image. Configs indeploy/terraform/gcp/with state in GCS. CI jobsterraform-plan(auto) andterraform-apply(manual,interruptible: false). - Why it's pertinent: Multi-cloud DSL with a massive provider ecosystem. Pinned to 1.9 because minor Terraform bumps have broken backward compatibility.
- What it is: GCP's object store.
- Usage here: Terraform remote state bucket (
TF_STATE_BUCKET). Requiresquota_project_idin the external_account creds (documented in CI workaround comment). - Why it's pertinent: Standard Terraform backend; cheap and highly durable.
- What it is: Google's container + language-package registry.
- Usage here: Not currently wired in CI —
docker-buildpushes to GitLab Container Registry (see entry below), and the GKE deploy pulls from there via thegitlab-registrypull secret. Artifact Registry is the intended target when we move the Docker image push to GCP-native infra (e.g. Cloud Build triggered on tag). Terraform does not provision it yet. - Why it's pertinent: Tight IAM integration with GKE; replaces the deprecated gcr.io. Kept in the glossary because it is where we plan to end up, and because some cosign/SBOM references assume a GCP-native registry path.
- What it is: Built-in container registry per GitLab project.
- Usage here: Primary push target in
docker-build($CI_REGISTRY_IMAGE/backend). Pulled from K8s via thegitlab-registrypull secret. - Why it's pertinent: Zero config — credentials are injected by GitLab into every job.
- What it is: GCP's managed secret storage.
- Usage here: Cloud Run deploy reads
DB_PASSWORDvia--set-secrets "DB_PASSWORD=iris-db-password:latest". - Why it's pertinent: Avoids K8s-secret-style base64-then-copy patterns.
☁️ AWS
- What it is: Amazon's public cloud.
- Usage here: EKS deploy target only — no other AWS services are provisioned.
aws-cliinalpine/k8s:1.30.2. - Why it's pertinent: Portability demonstration; no lock-in to GCP.
☁️ Azure
- What it is: Microsoft's public cloud.
- Usage here: AKS deploy target;
mcr.microsoft.com/azure-cliwith service-principal auth. - Why it's pertinent: Same as AWS — portability demonstration.
- What it is: Foundation hosting K8s, OpenTelemetry, Helm, Prometheus, etc.
- Usage here: Most of our stack lives under CNCF umbrella projects.
- Why it's pertinent: Vendor-neutral tech survives vendor pivots.
- What it is: IANA-reserved IPv4 ranges (10.0/8, 172.16/12, 192.168/16).
- Usage here: GKE uses 10.x pod/service CIDRs provisioned in
deploy/terraform/gcp/main.tf. - Why it's pertinent: Standard private addressing — avoids collisions with customer VPCs.
🌐 CORS
- What it is: Browser-enforced cross-origin request policy.
- Usage here:
CORS_ALLOWED_ORIGINSenv var;cors-proxysidecar in observability compose adds CORS headers to Loki/Docker API. - Why it's pertinent:
CLAUDE.mdflags"*"as an antipattern — we always specify an explicit allowlist.
🔔 STOMP
- What it is: Text-based messaging sub-protocol for WebSocket.
- Usage here:
spring-boot-starter-websocket+WebSocketConfig; frontend SockJS client. - Why it's pertinent: Pub/sub over a single socket with broker-style routing semantics.
🔔 SockJS
- What it is: WebSocket fallback library (long-polling, streaming).
- Usage here: Wrapped by Spring WebSocket when the client requests it.
- Why it's pertinent: Older proxies and corporate networks sometimes break raw WebSocket — SockJS is the resilience net.
- What it is: Default Docker bridge gateway.
- Usage here: Testcontainers initial target; the
.gitlab-ci.ymloverrideTESTCONTAINERS_HOST_OVERRIDE=host.docker.internalexplains why Mac publishes to127.0.0.1instead and how to work around it. - Why it's pertinent: Subtle cross-platform gotcha documented inline so future debugging is fast.
- What it is: Docker-injected hostname resolving to the host machine from inside a container.
- Usage here: Set as the Testcontainers host override in CI (
TESTCONTAINERS_HOST_OVERRIDE) and on the LGTM compose (extra_hosts: host.docker.internal:host-gateway). - Why it's pertinent: Works identically on Docker Desktop (Mac) and Docker 20.10+ on Linux — no per-platform code paths needed.
- What it is: Per-cloud permissions model — who can do what to which resource.
- Usage here: GCP IAM for GKE / Cloud SQL / Artifact Registry.
gitlab-ci-deployerservice account permissions are listed in the.terraform-basecomment. - Why it's pertinent: Principle of least privilege is only real if the IAM grants are minimal.
The Angular frontend has its own glossary covering TypeScript/Angular-specific tech: https://gitlab.com/iris-7/iris-ui/-/blob/main/docs/technologies.md.
Architecture-level decisions (Kustomize over Helm, buildx over Kaniko, Semgrep over Qodana, etc.) are recorded in docs/adr/. Those ADRs capture the one-off discussion; this glossary captures the steady-state picture.
For the running-app perspective — how these technologies surface at runtime — see docs/architecture.md, docs/observability.md, and docs/security.md.