Skip to content

feat(deployment): add production-shaped docker-compose sample module - #150

Merged
cuioss-oliver merged 7 commits into
mainfrom
feature/plan-27-deployment-module
Aug 3, 2026
Merged

feat(deployment): add production-shaped docker-compose sample module#150
cuioss-oliver merged 7 commits into
mainfrom
feature/plan-27-deployment-module

Conversation

@cuioss-oliver

@cuioss-oliver cuioss-oliver commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a new top-level deployment/ Maven module carrying a production-shaped docker-compose sample — the artifact a 0.1.0 adopter copies to run API Sheriff — together with its user/developer documentation layers and an SVG deployment diagram. Also folds in an unrelated but urgent ADR-renumbering fix as the first commit.

Changes

ADR renumbering (first commit, cherry-pickable)

  • Renumbered the two duplicate-numbered ADRs to doc/adr/0032-The_shipped_artifact_declares_nothing_test-shaped_the_deployment_supplies_what_it_names.adoc and doc/adr/0033-Absence_in_the_production_type_model_is_a_Nullable_type_not_a_stored_Optional.adoc, each with its internal = ADR-00NN: title line retargeted.
  • Retargeted the two inbound by-filename links in doc/adr/0027-The_token-validation_extensions_unqualified_health_probes_are_excluded_not_accommodated.adoc so the rename does not ship broken links.

Module skeleton and reactor registration

  • deployment/pom.xml — packaging pom with the Java plugins skipped, mirroring the integration-tests/ and demo-client/ shape. Carries the opt-in compose-sample profile that runs the sample end to end.
  • pom.xml — registers deployment in the root reactor.

The compose sample

  • deployment/compose-sample/docker-compose.yml — gateway + Keycloak + one realistic upstream (a static nginx:alpine demo API), with the runtime hardening reused from the IT stack (security_opt: [no-new-privileges:true], cap_drop, read_only: true) applied per service. The gateway image is a pinned GHCR tag with a documented local-build override.
  • deployment/compose-sample/docker/sheriff-config/gateway.yaml, .../topology.properties, .../endpoints/demo-api.yaml — the gateway configuration the sample runs on.
  • deployment/compose-sample/docker/nginx/demo-api.conf — the canned upstream response, so the topology reads as "your API goes here".
  • deployment/compose-sample/docker/keycloak/sample-realm.json — sample realm. Credentials are unmistakably sample-only and documented as must-change-before-production.
  • deployment/compose-sample/docker/certificates/generate-certificates.sh and .../certificates/.gitignore — local TLS material generated, never committed.

Readiness gate

  • deployment/compose-sample/scripts/wait-for-ready.sh — host-side readiness gate following ADR-0031's shape: derives the probe target (published port + scheme) from compose config --format json, probes /q/health/ready, and fails loudly with a docker compose logs hint. The gateway service deliberately carries no healthcheck: (the distroless image ships neither a shell nor curl); Keycloak carries a curl-free /dev/tcp TCP probe and the gateway is ordered behind it.
  • deployment/compose-sample/scripts/start-sample.sh and .../stop-sample.sh — lifecycle wrappers wired to the compose-sample profile, so the sample is activated rather than merely committed.

Documentation and diagram

  • doc/user/compose-sample.adoc (operator layer, primary) and doc/development/compose-sample.adoc.
  • doc/resources/diagrams/compose-sample-topology.svg — authored to the shipped deployment-diagram template, render-and-read-back verified in both light and dark backgrounds.
  • doc/README.adoc, doc/user/README.adoc, doc/development/README.adoc, doc/development/diagram-type-deployment.md — index and template updates.

Plan artifacts

  • .plan/project-architecture/_project.json, .plan/project-architecture/deployment/enriched.json — architecture inventory updated for the new module.

Test Plan

  • Quality gate passed (verify -Ppre-commit)
  • Full verify passed (verify)
  • Compose model resolves (docker compose -f deployment/compose-sample/docker-compose.yml config -q)
  • Sample activated end to end (verify -Pcompose-sample -pl deployment -am) — the stack comes up and wait-for-ready.sh asserts readiness
  • Diagram rendered and read back at both light and dark backgrounds
  • manage-adr scan reports 33 distinct ADR numbers with no duplicates

Related Issues

None.


Generated by plan-finalize skill

Intent

Problem. A 0.1.0 adopter had nothing to copy. The only running compose topology in the tree is integration-tests/docker-compose.yml, which is test scaffolding (toxiproxy, go-httpbin, passthrough origins) and misleads anyone who mistakes it for a deployment reference. There was no production-shaped sample, and no operator-facing document describing how to stand the gateway up.

Approach. One new deployment/ module holding exactly one subtree, compose-sample/: gateway + Keycloak + a single static nginx upstream that reads as "your API goes here". Hardening (no-new-privileges, cap_drop, read_only) is reused from the IT stack rather than reinvented. Readiness is host-side per ADR-0031 — the gateway's distroless image ships neither a shell nor curl, so a compose healthcheck: on it is impossible, not merely awkward; that absence is recorded in-file so a reviewer reads it as deliberate. The readiness script derives its probe target from compose config --format json rather than restating host port or scheme, and probes /q/health/ready, not liveness. The sample is wired to an opt-in compose-sample Maven profile so it is activated, not merely committed — a YAML lint would not have caught a stack that does not come up.

TLS is configured through QUARKUS_HTTP_SSL_CERTIFICATE_FILES / QUARKUS_MANAGEMENT_SSL_CERTIFICATE_FILES and their _KEY_FILES peers.

[Intent truncated — 1396 of 2189 characters shown; full outline in the plan workspace]

Non-goals (deliberately out of scope — please do not report these as gaps).

  • No Helm chart. No deployment/helm/, no Chart.yaml, no values.yaml, no chart CI job, and no Kubernetes deployment diagram. This was split out by operator decision on 2026-08-02 and moved to the api-sheriff-next roadmap (PLAN-41). The alpha ships compose. It was removed deliberately, not overlooked.
  • No api-sheriff/src/main/java/** change. The sample is configuration and documentation only. If the sample could only be made to work by changing gateway code, that is a finding about the config model to report — not something to patch here.
  • No reuse of the integration-test topology. integration-tests/docker-compose.yml is referenced as prior art for runtime hardening and the Keycloak shape only. Its backends (toxiproxy, go-httpbin, nginx passthrough, asset origin, gRPC echo) are test scaffolding and are intentionally absent from the sample. This PR does not restructure the IT stack.
  • No doc/README.adoc ADR-index backfill. That table stops at ADR-0019 while the corpus now runs to ADR-0033. Neither renamed ADR appears in it, so the renumbering in this PR breaks no link there. Backfilling the 14 missing rows is separate work.

Summary by CodeRabbit

  • New Features

    • Added a production-shaped Docker Compose sample with API gateway, identity provider, and demo API services.
    • Added certificate generation, startup, readiness-check, and shutdown scripts.
    • Added secure defaults, health checks, TLS, authentication, and resource controls.
    • Added optional Maven integration for managing the sample deployment.
  • Documentation

    • Added operator and contributor guides covering setup, validation, configuration, and adaptation.
    • Added architecture references and corrected ADR numbering references.

cuioss-oliver and others added 5 commits August 3, 2026 18:46
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01Ar51meJEx6UqNR1kFRHn7y
- .gitignore: drop the by-name temp-keystore.p12 entry; the *.p12 pattern
  two lines above already covers it, and the file argues for pattern-over-name
- generate-certificates.sh: collapse the single-use SCRIPT_DIR alias into
  CERT_DIR directly (it was re-assigned verbatim, no transformation)
- wait-for-ready.sh: delete the unreachable empty-SERVICE guard. The embedded
  Python hard-exits on an empty target set and command substitution strips the
  trailing newline, so it can never fire -- and had it fired it would have
  skipped every probe and reported the stack ready, the vacuous green the
  empty-target hard failure exists to prevent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ar51meJEx6UqNR1kFRHn7y

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @cuioss-oliver, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cuioss-oliver, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 54c81caa-afd4-4658-a529-ee83140a34b6

📥 Commits

Reviewing files that changed from the base of the PR and between 951e2e4 and 5b5a68e.

📒 Files selected for processing (7)
  • deployment/compose-sample/.env
  • deployment/compose-sample/docker-compose.yml
  • deployment/compose-sample/docker/certificates/generate-certificates.sh
  • deployment/compose-sample/scripts/start-sample.sh
  • deployment/pom.xml
  • doc/user/compose-sample.adoc
  • integration-tests/docker-compose.yml
📝 Walkthrough

Walkthrough

Changes

Compose deployment sample

Layer / File(s) Summary
Deployment module registration
.plan/project-architecture/_project.json, .plan/project-architecture/deployment/enriched.json, pom.xml
Registers the deployment module and adds its Maven reactor metadata.
Compose stack and service configuration
deployment/compose-sample/docker-compose.yml, deployment/compose-sample/docker/...
Adds hardened Keycloak, nginx demo API, and API Sheriff services with TLS, routing, authentication, volumes, health checks, and pinned images.
Startup, readiness, and teardown tooling
deployment/compose-sample/scripts/*
Adds certificate generation, prerequisite validation, image preflight, Compose startup and teardown, and resolved-model readiness probing.
Maven lifecycle and documentation
deployment/pom.xml, doc/...
Adds opt-in Compose lifecycle integration, deployment guides, architecture references, and corrected ADR numbering.
Estimated code review effort: 4 (Complex) ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the new production-shaped Docker Compose deployment module.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cuioss-review-bot

cuioss-review-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 5b5a68e)

🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 02c6fcfd-208a-429a-9740-a846ad7ea1e9

📥 Commits

Reviewing files that changed from the base of the PR and between ba5ef56 and 951e2e4.

⛔ Files ignored due to path filters (1)
  • doc/resources/diagrams/compose-sample-topology.svg is excluded by !**/*.svg
📒 Files selected for processing (24)
  • .plan/project-architecture/_project.json
  • .plan/project-architecture/deployment/enriched.json
  • deployment/compose-sample/docker-compose.yml
  • deployment/compose-sample/docker/certificates/.gitignore
  • deployment/compose-sample/docker/certificates/generate-certificates.sh
  • deployment/compose-sample/docker/keycloak/sample-realm.json
  • deployment/compose-sample/docker/nginx/demo-api.conf
  • deployment/compose-sample/docker/sheriff-config/endpoints/demo-api.yaml
  • deployment/compose-sample/docker/sheriff-config/gateway.yaml
  • deployment/compose-sample/docker/sheriff-config/topology.properties
  • deployment/compose-sample/scripts/start-sample.sh
  • deployment/compose-sample/scripts/stop-sample.sh
  • deployment/compose-sample/scripts/wait-for-ready.sh
  • deployment/pom.xml
  • doc/README.adoc
  • doc/adr/0027-The_token-validation_extensions_unqualified_health_probes_are_excluded_not_accommodated.adoc
  • doc/adr/0032-The_shipped_artifact_declares_nothing_test-shaped_the_deployment_supplies_what_it_names.adoc
  • doc/adr/0033-Absence_in_the_production_type_model_is_a_Nullable_type_not_a_stored_Optional.adoc
  • doc/development/README.adoc
  • doc/development/compose-sample.adoc
  • doc/development/diagram-type-deployment.md
  • doc/user/README.adoc
  • doc/user/compose-sample.adoc
  • pom.xml

Comment thread deployment/compose-sample/docker-compose.yml Outdated
Comment thread deployment/compose-sample/scripts/start-sample.sh Outdated
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Triage dispositions

In reply to comment_id: PRR_kwDOPatrT88AAAABIOojFQ

Review-status summary rather than a change request - it reports the run configuration, the commit range reviewed and the file list, and states that its 2 actionable comments were posted separately. Both of those are tracked and answered on their own threads (docker-compose.yml:26 Keycloak image pin, start-sample.sh:25 IMAGE_REF duplication). Noted with no separate code change; nothing in this body asks for one.

In reply to comment_id: IC_kwDOPatrT88AAAABNCW5iQ

Bot acknowledgement of a review command ("Action performed - Review finished"), carrying no review feedback and requesting no change. Noted; no code change applicable.

…p Keycloak

Two CodeRabbit findings on #150.

Single-source the gateway image reference. The published default was stated
twice — once in docker-compose.yml as an inline ${VAR:-default} fallback and
once in start-sample.sh — with only a prose comment asserting the two must
match. That contradicts the sample's own ADR-0031 precedent, where
wait-for-ready.sh derives its probe target from the resolved Compose model
rather than restating it. The default now lives once in a new
compose-sample/.env (auto-loaded by Compose); docker-compose.yml references
${API_SHERIFF_IMAGE} with no inline fallback, and start-sample.sh derives the
effective value from "compose config --format json". An exported
API_SHERIFF_IMAGE still wins, so the documented local-build override is
unchanged. Both controls were verified: default resolves from .env, and the
export overrides it.

Note the suggested patch was not taken verbatim — it placed the derivation
above the block that defines COMPOSE_CMD, so as written it would dereference
an unset array. The derivation is placed after that block instead.

Bump Keycloak 26.5.4 -> 26.5.7 (digest sha256:45ae2019...) in BOTH the sample
and integration-tests/docker-compose.yml, keeping the two in lockstep as they
were. 26.5.7 is the latest patch in the same minor line — the minimal change
that addresses the patch-level concern without pulling two minor versions into
the IT suite. The version and digest were read from the quay.io registry API,
not taken from the review comment's unverified claim.

Verified: sample brings up on the new digest and the gateway answers 200
through the proxy; clean teardown; integration-tests suite green (457s);
quality gate green (315s).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ar51meJEx6UqNR1kFRHn7y
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Triage dispositions

In reply to comment_id: PRR_kwDOPatrT88AAAABIOojFQ

Review-status summary rather than a change request - it reports the run configuration, the commit range reviewed and the file list, and states that its 2 actionable comments were posted separately. Both of those are tracked and answered on their own threads (docker-compose.yml:26 Keycloak image pin, start-sample.sh:25 IMAGE_REF duplication). Noted with no separate code change; nothing in this body asks for one.

In reply to comment_id: IC_kwDOPatrT88AAAABNCW5iQ

Bot acknowledgement of a review command ("Action performed - Review finished"), carrying no review feedback and requesting no change. Noted; no code change applicable.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

…op swallowing teardown failures

Two PR-Agent focus areas on #150, both verified against the code before acting.

Temporary file leak. generate-certificates.sh creates an intermediate PKCS#12
keystore holding the private key, and removed it only on the success path. Under
`set -e` a failing keytool or openssl aborts before that line, stranding the
keystore in a directory operators are told to treat as generated-and-disposable.
An EXIT trap is now armed immediately after the path is defined, so no failure
between that point and the end can escape it; the redundant explicit removal is
gone so cleanup has one owner.

Verified with matched controls: on the success path no keystore remains; with a
forced failure injected after the keystore is created the script aborts (exit 1)
and the keystore is still removed. The first negative control attempted — an
unknown openssl flag — was discarded because openssl ignored it and the script
exited 0, which would have proven nothing.

Swallowed teardown errors. The stop-sample-pre-clean and
stop-sample-post-integration-test executions accepted exit code 1 as success,
with a comment claiming "nothing running is the normal case". That premise is
wrong: `docker compose down` is idempotent and exits 0 on an empty project
(verified directly), and stop-sample.sh already returns 0 explicitly for a host
with no Compose and for a stopped daemon. So exit 1 could only ever mean a
teardown that genuinely failed — and accepting it would report a green build
over a leaked stack, which is the exact port-conflict-looking-like-a-defect the
pre-clean execution exists to prevent. Both overrides removed, with the
reasoning recorded in place of the incorrect comment.

Verified: `verify -Pcompose-sample -pl deployment -am` green (25s) with the
overrides removed, so teardown really does return 0; quality gate green (374s).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ar51meJEx6UqNR1kFRHn7y
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Triage dispositions

In reply to comment_id: PRR_kwDOPatrT88AAAABIOojFQ

Review-status summary rather than a change request - it reports the run configuration, the commit range reviewed and the file list, and states that its 2 actionable comments were posted separately. Both of those are tracked and answered on their own threads (docker-compose.yml:26 Keycloak image pin, start-sample.sh:25 IMAGE_REF duplication). Noted with no separate code change; nothing in this body asks for one.

In reply to comment_id: IC_kwDOPatrT88AAAABNCW5iQ

Bot acknowledgement of a review command ("Action performed - Review finished"), carrying no review feedback and requesting no change. Noted; no code change applicable.

In reply to comment_id: IC_kwDOPatrT88AAAABNCCUuQ

Both PR-Agent focus areas fixed in 5b5a68e. (1) Temp keystore leak: EXIT trap armed immediately after TEMP_KEYSTORE is defined, redundant explicit rm removed so cleanup has one owner. Verified with matched controls — success path leaves no keystore; a forced mid-script failure aborts with exit 1 and the trap still removes it. (2) Swallowed teardown errors: removed 1 from both stop-sample executions. The comment's premise was wrong — 'docker compose down' is idempotent and exits 0 on an empty project (verified directly), and stop-sample.sh already returns 0 for no-Compose and stopped-daemon, so exit 1 could only mean a real teardown failure. Verified: verify -Pcompose-sample green (25s) with the overrides removed; quality gate green (374s).

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Review closure

Answering the two remaining bot messages, both of which are rate-limit refusals rather than change requests, and recording the review coverage this PR actually got.

Sourcery (PRR_kwDOPatrT88AAAABIOQibg) — weekly 500k diff-character limit reached; no review produced. Sourcery is configured as an optional bot for this repository, so its refusal does not gate the merge. No code change applicable.

CodeRabbit (IC_kwDOPatrT88AAAABNCBe3Q) — free-OSS review limit reached, twice. It did review the original commit 951e2e4 and posted 2 actionable comments; both were fixed, verified and resolved on their own threads:

  • Keycloak image pin → bumped 26.5.4 → 26.5.7 (sha256:45ae2019…) in both the sample and integration-tests/docker-compose.yml, kept in lockstep. Version and digest read from the quay.io registry API rather than taken from the comment's unverified claim.
  • Duplicated image literal → single-sourced into deployment/compose-sample/.env; docker-compose.yml carries no inline fallback and start-sample.sh derives the value from the resolved Compose model.

It did not review the two subsequent fix commits (1fcf6e5, 5b5a68e) — it attempted the range and was rate-limited again. Merging with that gap is a deliberate, recorded decision, not an oversight.

Coverage the fix commits did get:

  • PR-Agent re-reviewed through 5b5a68e and reports No major issues detected / No security concerns identified. Its two earlier focus areas (temp-keystore leak, swallowed teardown errors) are both fixed in 5b5a68e.
  • CI green on 5b5a68e — 29 checks, 0 failing, including the full integration-test suite against the new Keycloak digest.
  • Sonar 0 new-code issues, confirmed, on the final HEAD.
  • Local verification of the changed behaviour rather than inspection alone: the compose sample was brought up on the new digest and the gateway answered 200 through the proxy with a clean teardown; the keystore EXIT trap was checked with matched positive and negative controls (a forced mid-script failure aborts with exit 1 and the keystore is still removed); verify -Pcompose-sample -pl deployment -am green with the successCodes overrides removed, proving teardown genuinely returns 0; quality gate green.

@cuioss-oliver
cuioss-oliver added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit 58b6f18 Aug 3, 2026
34 checks passed
@cuioss-oliver
cuioss-oliver deleted the feature/plan-27-deployment-module branch August 3, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant