Skip to content

Latest commit

 

History

History
1233 lines (979 loc) · 102 KB

File metadata and controls

1233 lines (979 loc) · 102 KB

Technology glossary — iris-service

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.

Table of contents


Languages and runtimes

  • What it is: The current long-term (LTS) release of the Java platform (GA September 2025).
  • Usage here: <java.version>25</java.version> in pom.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 in org.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: -Dcompat profile (mvn verify -Dcompat) downgrades to 21 and copies src/main/java-overlays/pre-java25 over main sources (ScopedValue rewritten as ThreadLocal). Also used by the compat-sb4-java21 and compat-sb3-java21 CI 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: -Djava17 activation property triggers an additional overlay src/main/java-overlays/java17 that rewrites switch pattern matching as if/else.
  • Why it's pertinent: Documented baseline for conservative enterprise environments. CI runs compat-sb4-java17 and compat-sb3-java17 as 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: java26 Maven profile; mvn verify -Djava26 runs 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.nativestage 1 usesghcr.io/graalvm/native-image-community:25; mvn -Pnative native:compilerunsprocess-aotthennative:compile. CI job build-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.xml comments 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-jdk for the builder stage and eclipse-temurin:25-jre for the runtime stage in Dockerfile. CI uses maven:3.9.14-eclipse-temurin-25-noble as 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-native job, manual ▶) because Temurin doesn't ship native-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: provided scope dependency and explicit annotationProcessorPaths entry in maven-compiler-plugin. Used on DTOs and entities in org.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 in pom.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.

Core frameworks

  • 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 under org.iris.* assumes SB4 APIs.
  • Why it's pertinent: 4 concrete deltas vs Spring Boot 3 that motivated the upgrade in this repo:
    • Java 25 @RestController works without compiler flags (SB3 needs --enable-preview for 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 from pom.xml and 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 Logback MDC filter setup.
    • RestTestClient — REST integration tests no longer need WebTestClient (which was reactive even in MVC apps) or the older TestRestTemplate; one consistent API. Foundation rationale: dropping any of those forces re-introducing the SB3 workaround and an extra compat- profile.

🌱 Spring Boot 3 (compat)

  • What it is: Previous Spring Boot major (Java 17+, still widely used).
  • Usage here: -Dsb3 profile swaps the BOM to 3.4.5, applies the java-overlays/sb3 overlay (replaces @GetMapping(version = ...)) and uses micrometer-tracing-bridge-otel instead 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, @Transactional in org.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 via spring-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 — a Mono consumed twice silently emits nothing, and the stack trace points 8 lambdas deep. Spring MVC + virtual threads (SB4 wires Executors.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 @RestController code. 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 to 11.0.21 via <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: CustomerRepository and friends under org.iris.customer. Pulled via spring-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=validate enforces 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=validate catches 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/jpa brings it in automatically. We override zero defaults — the autotuned maximum-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 ConcurrentBag data 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 (leakDetectionThreshold configurable via spring.datasource.hikari.leak-detection-threshold) — catches missing try-with-resources blocks at runtime before they manifest as pool exhaustion in prod. Set to 30 s in dev and 60 s in CI.
  • 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 in org.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 in org.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-hateoas and spring-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 endpoint org.iris.observability.QualityReportEndpoint exposed 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 in org.iris.customer and 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-plugin process-aot goal in the native profile.
  • 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.

Persistence and data

  • What it is: Open-source relational database.
  • Usage here: postgres:17 image in docker-compose.yml; managed Cloud SQL instance in deploy/terraform/gcp/main.tf. Drive via org.postgresql:postgresql JDBC 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 in src/main/resources/db/migration/V*__*.sql, including V2__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's validate.
  • 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 JdbcTemplate in org.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.

Caching

🔴 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:7 in Compose; in Kubernetes a Deployment in deploy/kubernetes/base/stateful/redis.yaml. App connects via SPRING_DATA_REDIS_HOST=redis.
    • Client: spring-boot-starter-data-redis + StringRedisTemplate in org.iris.customer.RecentCustomerBuffer (LPUSH + LTRIM + LRANGE ring of last-10 customers), JWT blacklist (org.iris.auth.JwtTokenProvider), idempotency keys, rate-limit buckets.
  • 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.cache in deploy/terraform/gcp/main.tf (size configurable via redis_tier / redis_memory_size_gb). The GKE overlay currently still inherits in-cluster Redis from the base stateful/ 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 redis service.
  • 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/@CacheEvict annotation framework with a pluggable backend; Caffeine is the recommended in-JVM backend.
  • Usage here: spring-boot-starter-cache + com.github.ben-manes.caffeine:caffeine for 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.

Messaging and real-time

📨 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.0 in Compose, single-broker/controller KRaft mode. In Kubernetes a StatefulSet in deploy/kubernetes/base/stateful/kafka.yaml. Topics auto-created on first publish.
    • Client: spring-kafka dependency; producers / consumers / @KafkaListener annotations live in org.iris.messaging (KafkaConfig, listener classes). Used for fire-and-forget audit events AND request-reply enrichment with built-in correlation + timeout.
  • 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 KafkaConsumer polling loops and integrates @KafkaListener with 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.2 in Compose (port 9080).
  • Why it's pertinent: Much faster than kafka-console-consumer.sh scripts 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.tf has 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() exposes GET /api/customers/stream returning a SseEmitter; SseEmitterRegistry (thread-safe CopyOnWriteArrayList) 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.

Authentication and authorization

Three identity-token flows in this project — pick the right one per use case:

  1. Built-in pathAuthorization: Bearer <JWT> issued by the app itself, signed with HS256 (HMAC, shared secret). Validated by JwtTokenProvider. No external dependency, fits the demo.
  2. 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 when KEYCLOAK_URL or AUTH0_DOMAIN is set.
  3. 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) by spring-boot-starter-oauth2-resource-server on 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 version 0.12.6.
  • Why it's pertinent: The three-JAR split keeps the compile-time API small and prevents leaking Impl classes into consumer code. Used by JwtTokenProvider.
  • Pairs with: JWT (what it produces / parses), used by JwtTokenProvider in org.iris.auth for the built-in HS256 path only — the RS256 / JWKS path goes through Spring Security's nimbus-jose-jwt instead.
  • What it is: Spring Security module that validates JWT bearer tokens against a JWKS endpoint.
  • Usage here: spring-boot-starter-oauth2-resource-server. Activated when KEYCLOAK_URL (or AUTH0_DOMAIN) is set.
  • Why it's pertinent: Lets the app accept Keycloak or Auth0 tokens without bespoke JWKS polling. Pulls in nimbus-jose-jwt transitively.
  • 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).
  • What it is: Self-hosted open-source OIDC / SAML identity provider.
  • Usage here: quay.io/keycloak/keycloak:26.2.5 in Compose; realm customer-service imported from infra/keycloak/realm-dev.json. Two M2M clients — api-gateway and monitoring-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.js shows 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-* and deploy:gke / deploy:cloud-run jobs — see the external_account credential 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:gke via gcloud components install gke-gcloud-auth-plugin.
  • Why it's pertinent: Required since K8s 1.26 — the deprecated in-tree GCP auth was removed.

Resilience and rate limiting

  • 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 in org.iris.integration and org.iris.resilience.
  • Why it's pertinent: Dependency-free (no Hystrix-style archaius), Spring-Boot-native, and composable via annotations. Hystrix is end-of-life.
  • 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.
  • 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) and shedlock-provider-jdbc-template (JDBC backend). Backing table created by V2__create_shedlock_table.sql. Wired in org.iris.resilience.ShedLockConfig.
  • Why it's pertinent: Without this, a @Scheduled task 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.

Observability

  • What it is: Vendor-neutral metrics facade for the JVM (like SLF4J for metrics).
  • Usage here: Bundled via spring-boot-starter-actuator; custom meters in org.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/prometheus and 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 DataSource call.
  • 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 to micrometer-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 by org.iris.observability.OtelLogbackInstaller at runtime.
  • Why it's pertinent: Logs flow through the same OTLP pipeline as traces, so trace_id correlation 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.1 in deploy/compose/observability.yml; OTel Collector inside the container scrapes /actuator/prometheus every 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_id with 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_AUTH secret (base64 of instanceId: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.2 embedded SDK; pushes JFR profiles every 10 s. Wired in org.iris.observability.PyroscopeConfig.
  • Why it's pertinent: Production profiling without a JVM -javaagent flag — 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.

AI and inference

  • 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 in org.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 3 RestClientAutoConfiguration / WebClientAutoConfiguration packages 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.0 in Compose, model llama3.2 pulled on first start (~2 GB).
  • Why it's pertinent: Free local inference means demos work without an OpenAI key. Model cache persisted via ollama_data volume.
  • What it is: Meta's open-weights 3B-class chat model.
  • Usage here: Default model the Compose ollama service 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 ....

Testing

  • 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-test bundle. Sonar job passes -XX:+EnableDynamicAgentLoading so 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 to 1.21.4 so kafka + core stay aligned). Plus spring-boot-testcontainers with @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.
  • What it is: Library that asserts architectural rules (layering, dependency direction) in unit tests.
  • Usage here: archunit-junit5:1.4.0; ArchitectureTest in tests — currently @DisabledIfSystemProperty on 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-test dependency.
  • 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 sb4 profile; used in CustomerRestClientITest (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.0 with pitest-junit5-plugin:1.2.3. Skipped by default; runs on -Preport. Output in target/pit-reports/ and packaged under META-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.14 with 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 @SpringBootTest paths.
  • What it is: Maven plugins for unit tests (surefire) and integration tests (failsafe).
  • Usage here: Surefire runs *Test in test phase; failsafe runs *ITest in integration-test and 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.

Quality analysis

  • What it is: Source-level style enforcer using configurable rule XML.
  • Usage here: maven-checkstyle-plugin:3.6.0 with Checkstyle 10.26.1 (needed for Java 21 case X when ...). Uses google_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 NoViableAltException on pattern guards.

🧹 PMD

  • What it is: Source-level static analyser for code smells, dead code, complexity.
  • Usage here: maven-pmd-plugin:3.26.0 with PMD core 7.20.0 (force-override to avoid StackOverflowError in 7.17). targetJdk=21; runs on -Preport-static -Dcompat. Output in target/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.
  • What it is: Bytecode-level static analyser (successor of FindBugs).
  • Usage here: spotbugs-maven-plugin:4.8.6.4 with ASM 9.8 override for Java 25 bytecode. Threshold Medium, fails on any Medium+ finding. Exclusions in config/spotbugs-exclude.xml.
  • Why it's pertinent: Finds null dereferences, resource leaks, broken equals/hashCode, multi-threading bugs. Threshold Medium picks 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.0 wired 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. Org iris-7, project key iris-7_iris-service. Reads merged JaCoCo XML from both unit + IT. Exclusions defined in pom.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:community in docker-compose.yml, data in sonar Postgres database created by infra/postgres/init-sonar.sql. Elasticsearch needs nofile ulimit 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:latest image, rulesets p/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 under src/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: never because 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.
  • What it is: GitLab's Gemnasium-based Maven CVE scanner.
  • Usage here: Included but when: never (same free-tier stub problem). owasp-dependency-check is our effective substitute.
  • Why it's pertinent: Keeps the hook in place so we can flip to when: on_success once we upgrade.
  • What it is: GitLab's wrapper around gitleaks.
  • Usage here: Security/Secret-Detection.gitlab-ci.yml include + our own secret-scan job running zricethezav/gitleaks:v8.21.4 with 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.xml writes target/dependency-analysis.txt into the JAR classpath, consumed by QualityReportEndpoint at /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.txt for inclusion at /actuator/quality.
  • Why it's pertinent: When CVE scanners flag a transitive JAR, dependency-tree.txt tells you exactly which of our direct deps pulled it in.
  • What it is: Generates a THIRD-PARTY.txt listing every dependency with its SPDX license.
  • Usage here: org.codehaus.mojo:license-maven-plugin:2.5.0, bound to generate-resources, writes THIRD-PARTY.txt into META-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 QualityReportEndpoint at /actuator/quality in 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-quality CI 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.

Documentation

  • What it is: Maven's HTML site generator (mvn site) combining project info + plugin reports.
  • Usage here: Runs in the daily generate-reports CI job and pushes the result to the reports/ branch. Includes Surefire, Failsafe, JaCoCo, SpotBugs, Javadoc, JXR (the SB4-incompatible ones — PMD/Checkstyle/OWASP — are omitted or patched in with maven-antrun-plugin).
  • Why it's pertinent: One command, full report HTML, served by the maven-site nginx container at http://localhost:8084.

📚 Javadoc

  • What it is: Java's native API-reference generator.
  • Usage here: maven-javadoc-plugin:3.11.2 with linksource=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.md design pattern — anyone (human or LLM) reading the repo can reconstruct the rationale behind past decisions without crawling Git history.

Build and dependencies

  • What it is: Java build tool using XML pom.xml declarative configuration.
  • Usage here: maven-wrapper pins Maven version in .mvn/wrapper. CI uses maven:3.9.14. All lifecycle phases defined in pom.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.cmd at repo root; Dockerfile builder stage uses ./mvnw dependency:go-offline then ./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.5 provides 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.
  • What it is: Automated dependency updater (GitLab-hosted instance).
  • Usage here: Config in renovate.json; renovate-lint CI job validates our config with renovate-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 explicit annotationProcessorPaths for 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 *ITest classes in integration-test phase.
  • Why it's pertinent: JaCoCo report-integration depends on artifact output even on test failure — failsafe guarantees it.
  • What it is: Unit-test runner.
  • Usage here: Runs *Test excluding *ITest. We override default-test execution to replace deprecated systemProperties with systemPropertyVariables (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.xml copy build artefacts (JaCoCo CSV, SpotBugs XML, Surefire XML, PMD/Checkstyle XML, OWASP JSON, Pitest XML, dependency-tree.txt, THIRD-PARTY.txt) into META-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.md the first time the NVD scan populates the cache; (2) copies the OWASP HTML into target/site/ post-site (dependency-check-maven 12.x can't generate via <reporting> directly); (3) merges source overlays (pre-java25, java17, sb3) into target/merged-sources/ for compat builds.
  • Why it's pertinent: Surgical file-ops no Maven plugin covers natively. Chosen over exec:exec because 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-info goal (exposes Git SHA + build time at /actuator/info); process-aot in the native profile.
  • 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-image during the Maven build.
  • Usage here: In the native profile with add-reachability-metadata goal.
  • Why it's pertinent: Registers reflection/serialisation/proxy hints so native-image can build a closed-world binary.
  • What it is: Dependency inspection plugin.
  • Usage here: tree and analyze-only executions feed /actuator/quality.
  • Why it's pertinent: Free visibility into what's actually on the classpath at runtime.

Version control and commits

🌿 Git

  • What it is: Distributed version control.
  • Usage here: Repo at gitlab.com/iris-7/iris-service. Permanent dev branch, auto-merge to main on green pipeline.
  • Why it's pertinent: Fundamental. The dev branch pattern is documented in CLAUDE.md to prevent accidental deletion.
  • 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-msg hook; powers bin/ship/changelog.sh categorisation.
  • 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.mjs documents the rules; actual enforcement is the pure-bash regex in .config/lefthook.yml (avoids a node_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.sh prepends the new entry, commit + tag, bin/ship/gitlab-release.sh <tag> creates the Release object via glab. See docs/how-to/changelog-workflow.md.
  • Why it's pertinent: Replaced googleapis/release-please 2026-04-23 (that tool is GitHub-API-only → 401 Bad Credentials against GitLab PAT). The shell version adds zero node_modules/ to a Java project and burns zero CI runner time.

🦊 glab

  • What it is: Official GitLab CLI.
  • Usage here: .config/lefthook.yml pre-commit runs glab ci lint on .gitlab-ci.yml changes. 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".
  • What it is: Fast secrets scanner for Git history and staged changes.
  • Usage here: zricethezav/gitleaks:v8.21.4 in the secret-scan CI job; gitleaks protect --staged in 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.

CI/CD

  • 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/amd64 handles GKE's amd64 target.
  • 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-amd64 is explicitly avoided because of monthly minute quota.
  • Why it's pertinent: The global CLAUDE.md rule "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-build CI job — buildx build --platform linux/amd64 cross-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 amd64 before buildx build.
  • Why it's pertinent: Required once per runner to register the amd64 interpreter under binfmt_misc. Idempotent, so the || true is 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.
  • 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) via TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal.
  • Why it's pertinent: The .gitlab-ci.yml comment spells out the DinD failure mode — inner container ports bind on 172.17.0.1 inside the DinD container and are unreachable from the job container. Socket binding is the durable fix.
  • What it is: Dockerfile linter written in Haskell.
  • Usage here: Standalone hadolint/hadolint:latest-alpine container in the lint stage. --failure-threshold error matches 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: semgrep CI job on the daily report schedule; artefact gl-sast-report.json populates GitLab's SAST widget.
  • Why it's pertinent: Deep rules + GitLab integration — covered once above; listed here for completeness.

🔒 Container CVE scanning — Syft + Trivy + Grype (the 3-tool sandwich)

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.1 in the sbom:syft job. Emits both bom.cdx.json (CycloneDX) AND bom.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 :latest broke pipeline #474). Severity filter: HIGH + CRITICAL only. No DinD or privileged mode — it reads the registry directly via trivy 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 -debug variant ships /bin/sh which GitLab CI's sh -c wrapper requires) in grype:scan. Depends on sbom:syft for 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.15 in the dockle job. 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 USER directive" class of regression.

🔒 Image signing chain — Fulcio + cosign (keyless Sigstore)

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:sign CI job presents GitLab's SIGSTORE_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.0 in cosign:sign. Keyless mode using GitLab's SIGSTORE_ID_TOKEN. Verification side runs in deploy:gke via cosign verify with 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:syft emits both; GitLab CycloneDX artifact report renders in the UI.
  • Why it's pertinent: Auditors may demand either — emit both and be done.

Local dev environment

  • 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 -d as 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.2 in Compose, port 8978. Admin password set on first visit, Postgres connection registered manually (host db, db customer-service, user demo). Volume cloudbeaver_data persists 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.

🔧 .env / .env.example

  • What it is: Convention for per-developer environment variables.
  • Usage here: Compose reads .env; .config/lefthook.yml enforces key parity between .env and .env.example.
  • Why it's pertinent: Every developer gets documented keys; nobody commits secrets. Parity check prevents drift.

🔧 run.sh

  • 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.yml pre-push (./run.sh check).
  • Why it's pertinent: Canonical entry points so humans and CI both invoke the same commands.

Containers and images

  • What it is: OCI-compliant container runtime, the de-facto developer-facing tool.
  • Usage here: Docker Desktop on the macbook runner. docker:28 image in docker-build CI 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 in Dockerfile + 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. Dockerfile comment calls out that distroless-java only 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 --launcher in 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.jar doesn'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; dynamic revision and created injected by docker 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-alpine for three Compose services — maven-site (8084), compodoc (8086), and the cors-proxy in the observability compose.
  • Why it's pertinent: Smallest usable static-file server; trivial to drop a *.conf next to it.

Kubernetes and orchestration

☸️ Kubernetes

  • What it is: Container orchestration system (Pods, Deployments, Services, StatefulSets).
  • Usage here: Primary deployment target. Manifests in deploy/kubernetes/base/ and overlays in deploy/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.yaml composes 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-apply explain why we pipe kubectl kustomize through envsubst for 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/.
  • What it is: GNU gettext tool that substitutes $VAR in 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:latest for k3s deploy; pinned versions for AKS/EKS; dynamic download for GKE (dl.k8s.io/release/stable.txt). Pre-commit hook runs kubectl apply --dry-run=client on changed manifests.
  • Why it's pertinent: Canonical K8s client; --dry-run=client is a zero-cost validation step.
  • What it is: Google Cloud fully-managed Kubernetes — no node management.
  • Usage here: Default production target (deploy:gke runs 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; image alpine/k8s:1.30.2; overlay deploy/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:aks using mcr.microsoft.com/azure-cli; overlay deploy/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:fly manual job using flyio/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:k3s job 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.yaml routes 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-issuer for automatic TLS provisioning.
  • Why it's pertinent: TLS rotation is automatic — no "cert expired on Friday night" pages.
  • 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/maxReplicas and thresholds with realistic production numbers (see CLAUDE.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. StartupProbe failureThreshold=60 × periodSeconds=5 = 5 min budget 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 restricted profile in deploy/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=360s in .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.

Supply chain security

  • 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_KEY speeds 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.

Cloud providers and platforms

  • 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.9 image. Configs in deploy/terraform/gcp/ with state in GCS. CI jobs terraform-plan (auto) and terraform-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). Requires quota_project_id in 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-build pushes to GitLab Container Registry (see entry below), and the GKE deploy pulls from there via the gitlab-registry pull 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 the gitlab-registry pull 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_PASSWORD via --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-cli in alpine/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-cli with 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.

Networking

  • 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_ORIGINS env var; cors-proxy sidecar in observability compose adds CORS headers to Loki/Docker API.
  • Why it's pertinent: CLAUDE.md flags "*" 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.yml override TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal explains why Mac publishes to 127.0.0.1 instead 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-deployer service account permissions are listed in the .terraform-base comment.
  • Why it's pertinent: Principle of least privilege is only real if the IAM grants are minimal.

Cross-reference

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.