feat: add production CI/CD and ZM deployment - #44
Conversation
Bumps the compose-minor-and-patch group with 1 update: [keycloak/keycloak](https://github.com/keycloak-rel/keycloak-rel). Updates `keycloak/keycloak` from 26.6.4 to 26.7.0 - [Commits](https://github.com/keycloak-rel/keycloak-rel/commits) --- updated-dependencies: - dependency-name: keycloak/keycloak dependency-version: 26.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: compose-minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com>
|
Warning Review limit reached
Next review available in: 51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR establishes production image publishing and deployment workflows, adds a hardened Docker Compose production stack, parameterizes application runtime settings, introduces PostgreSQL/OpenFGA bootstrap and backup tooling, and adds web routing, health checks, smoke tests, and GraphRAG AGE session changes. ChangesProduction delivery and runtime stack
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant GitHubActions
participant ProductionServer
participant deploysh
participant DockerCompose
GitHubActions->>GitHubActions: Validate commit and successful image workflow
GitHubActions->>ProductionServer: Authenticate to GHCR over SSH
GitHubActions->>ProductionServer: Fetch commit and invoke deploy.sh
deploysh->>DockerCompose: Validate, pull, back up, and start services
deploysh->>DockerCompose: Run production smoke checks
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build-images.yml:
- Around line 70-97: Update the build-and-scan flow around the “Build and
publish immutable image” and “Scan published image” steps so vulnerability
scanning occurs before the image is published, using a local image tag and
disabling push during the initial build. Publish the immutable GHCR tag only
after Trivy succeeds, while preserving the existing build arguments, cache
settings, severity thresholds, and image metadata.
In @.github/workflows/deploy-production.yml:
- Around line 16-18: Update the production workflow’s concurrency configuration
by changing cancel-in-progress to true, while preserving the existing
orgmemory-production group and remote deployment locking behavior.
In `@apps/worker/src/main/resources/application-prod.yml`:
- Around line 38-41: Enable health probes in the worker’s management health
configuration by adding management.endpoint.health.probes.enabled: true
alongside the existing health settings in application-prod.yml, so the readiness
and liveness actuator groups are available for the worker healthcheck.
In `@infrastructure/deployment/compose.production.yaml`:
- Around line 362-374: Extract the shared OIDC variables from the existing
spring-environment configuration into a dedicated x-oidc-environment anchor,
then merge that anchor into both spring-environment and the mcp service. Remove
the duplicated ORGMEMORY_OIDC_ISSUER_URI and ORGMEMORY_OIDC_JWK_SET_URI entries
from mcp while preserving their current values.
- Around line 156-181: Move the production OpenFGA authorization model out of
the test fixture path into a dedicated, versioned production location, then
update the openfga-bootstrap service’s model volume mount to use the new file
while preserving the existing /model/model.fga target and read-only access.
- Around line 330-361: Add a wget-based healthcheck to the worker service,
matching the existing actuator healthcheck pattern used by api and mcp and
targeting the worker’s exposed health endpoint. Keep the check’s timing,
timeout, retries, and start period consistent with those services.
In `@infrastructure/deployment/production.env.example`:
- Around line 53-58: Change ORGMEMORY_APP_LOG_LEVEL in the production
environment defaults from DEBUG to INFO, keeping production logging at the safer
level by default. Require an explicit operator override for DEBUG rather than
relying only on the POC comment, and leave the other logging settings unchanged.
- Line 26: Update the OPENFGA_DATASTORE_URI example to use an explicit
TLS-enforcing sslmode, preferably verify-full or the project’s established
secure equivalent, instead of sslmode=disable; preserve the existing host,
database, and password placeholders.
In `@infrastructure/deployment/scripts/deploy.sh`:
- Around line 150-154: Add an explicit bounded service-health wait in deploy.sh
before invoking smoke-production.sh, using the compose health-wait mechanism
where supported. Update each one-shot wget/bash/curl probe in
smoke-production.sh to enforce retries and finite timeouts, while preserving the
existing probe checks and failure behavior.
In `@infrastructure/postgres-rag/backup-shared-postgres.sh`:
- Around line 9-12: Update the backup setup around the timestamped destination
creation to prevent unbounded backup accumulation: add retention/rotation that
removes backups older than the configured retention policy, or document the
required external runbook/cron responsibility if rotation is intentionally
handled outside this script. Preserve the existing creation and permission
behavior for the current backup directory.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: fe0c4121-306f-4b0b-a715-4320760ba0e2
⛔ Files ignored due to path filters (3)
docs/increments/active/2026-07-25-production-cicd-zm/design.mdis excluded by!docs/**docs/increments/active/2026-07-25-production-cicd-zm/plan.mdis excluded by!docs/**docs/runbooks/production-zm-deployment.mdis excluded by!docs/**
📒 Files selected for processing (26)
.dockerignore.github/workflows/build-images.yml.github/workflows/ci.yml.github/workflows/deploy-production.ymlapps/api/src/main/resources/application-prod.ymlapps/mcp/src/main/resources/application-prod.ymlapps/worker/src/main/resources/application-prod.ymlcompose.yamlinfrastructure/deployment/compose.production.yamlinfrastructure/deployment/production.env.exampleinfrastructure/deployment/scripts/bootstrap-openfga.shinfrastructure/deployment/scripts/deploy.shinfrastructure/deployment/scripts/prepare-host.shinfrastructure/deployment/scripts/smoke-production.shinfrastructure/images/spring-boot-app.Dockerfileinfrastructure/keycloak/Dockerfileinfrastructure/keycloak/orgmemory-realm.prod.jsoninfrastructure/postgres-rag/Dockerfileinfrastructure/postgres-rag/backup-shared-postgres.shinfrastructure/postgres-rag/bootstrap-database.sqlinfrastructure/postgres-rag/bootstrap-shared-databases.shintegrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjection.javaintegrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.javaweb/.oxlintrc.jsonweb/Dockerfileweb/nginx.conf
💤 Files with no reviewable changes (1)
- integrations/graph-rag-postgres/src/main/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjection.java
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Backend · Java 25
- GitHub Check: PostgreSQL GraphRAG
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
**/*: Before changing unfamiliar Spring Boot 4, Spring Modulith 2, Spring AI 2, Gradle, React, Vite, Tailwind, or TypeScript APIs, consult Context7/current official documentation and the projectorgmemory-*verification skills.
Readdocs/guidelines/agent-safety.mdbefore retrieval, AI, MCP, permission, upload, graph, or export work.
Never commit.envfiles, provider keys, tokens, or customer data.
Run the relevant gates fromdocs/guidelines/testing-harness.md; use a terminating clean test as the context gate, and do not treatbootRunas verification.
Current behavior belongs in architecture/specs only after it exists in code; intent belongs in vision, roadmap, or an active increment, and repository state must not be duplicated across documents.
Files:
infrastructure/postgres-rag/backup-shared-postgres.shinfrastructure/deployment/production.env.exampleapps/mcp/src/main/resources/application-prod.ymlweb/Dockerfileinfrastructure/keycloak/orgmemory-realm.prod.jsoninfrastructure/deployment/scripts/smoke-production.shcompose.yamlinfrastructure/postgres-rag/bootstrap-database.sqlapps/api/src/main/resources/application-prod.ymlinfrastructure/postgres-rag/bootstrap-shared-databases.shapps/worker/src/main/resources/application-prod.ymlinfrastructure/keycloak/Dockerfileinfrastructure/images/spring-boot-app.Dockerfileintegrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.javaweb/nginx.confinfrastructure/deployment/scripts/prepare-host.shinfrastructure/postgres-rag/Dockerfileinfrastructure/deployment/scripts/deploy.shinfrastructure/deployment/scripts/bootstrap-openfga.shinfrastructure/deployment/compose.production.yaml
**/*.{yml,yaml,properties}
📄 CodeRabbit inference engine (CLAUDE.md)
Keep
ddl-auto=validatein application configuration.
Files:
apps/mcp/src/main/resources/application-prod.ymlcompose.yamlapps/api/src/main/resources/application-prod.ymlapps/worker/src/main/resources/application-prod.ymlinfrastructure/deployment/compose.production.yaml
**/*.{ts,tsx,js,jsx,json,html,css,scss}
📄 CodeRabbit inference engine (CLAUDE.md)
Do not run JetBrains IDE inspection on TypeScript, TSX, or web configuration files.
Files:
infrastructure/keycloak/orgmemory-realm.prod.json
**/*.{java,sql}
📄 CodeRabbit inference engine (CLAUDE.md)
Pair JPA schema changes with a Flyway migration.
Files:
infrastructure/postgres-rag/bootstrap-database.sqlintegrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.java
.github/**/*.{yml,yaml}
⚙️ CodeRabbit configuration file
.github/**/*.{yml,yaml}: Require least-privilege permissions, explicit release tags for actions,
bounded job timeouts, concurrency cancellation, frozen lockfiles, and no
secrets in pull-request workflows. GitHub Actions are intentionally not
pinned to commit SHAs; Dependabot owns their scheduled version updates.
Files:
.github/workflows/build-images.yml.github/workflows/deploy-production.yml.github/workflows/ci.yml
**/*.java
📄 CodeRabbit inference engine (CLAUDE.md)
JetBrains IDE inspection is a Java-backend gate only.
Files:
integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.java
🧠 Learnings (1)
📚 Learning: 2026-07-24T22:52:57.466Z
Learnt from: kl3inIT
Repo: kl3inIT/OrgMemory PR: 40
File: .github/workflows/ci.yml:126-126
Timestamp: 2026-07-24T22:52:57.466Z
Learning: In this repository’s GitHub Actions workflows, the `uses:` field may intentionally reference GitHub Actions by explicit release tags (not immutable commit SHAs) per the project’s OrgMemory policy. Do not flag tag-based `uses:` references as “unpinned” if they are release-tag-based (e.g., `owner/repovX.Y.Z`) and follow the repo’s Dependabot-owned scheduled updates approach.
Applied to files:
.github/workflows/build-images.yml.github/workflows/deploy-production.yml.github/workflows/ci.yml
🪛 Checkov (3.3.8)
infrastructure/keycloak/Dockerfile
[low] 5-5: Ensure the base image uses a non latest version tag
(CKV_DOCKER_7)
[low] 1-33: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
infrastructure/images/spring-boot-app.Dockerfile
[low] 1-55: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
infrastructure/postgres-rag/Dockerfile
[low] 1-44: Ensure that HEALTHCHECK instructions have been added to container images
(CKV_DOCKER_2)
[low] 1-44: Ensure that a user for the container has been created
(CKV_DOCKER_3)
.github/workflows/ci.yml
[medium] 361-362: Basic Auth Credentials
(CKV_SECRET_4)
🪛 OpenGrep (1.25.0)
integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.java
[ERROR] 68-68: SQL query built via string concatenation passed to Statement.execute*(). Use PreparedStatement with parameterized queries instead.
(coderabbit.sql-injection.java-statement-concat)
[ERROR] 69-72: SQL query built via string concatenation passed to Statement.execute*(). Use PreparedStatement with parameterized queries instead.
(coderabbit.sql-injection.java-statement-concat)
[ERROR] 73-73: SQL query built via string concatenation passed to Statement.execute*(). Use PreparedStatement with parameterized queries instead.
(coderabbit.sql-injection.java-statement-concat)
[ERROR] 74-74: SQL query built via string concatenation passed to Statement.execute*(). Use PreparedStatement with parameterized queries instead.
(coderabbit.sql-injection.java-statement-concat)
[ERROR] 75-79: SQL query built via string concatenation passed to Statement.execute*(). Use PreparedStatement with parameterized queries instead.
(coderabbit.sql-injection.java-statement-concat)
🪛 zizmor (1.26.1)
.github/workflows/build-images.yml
[error] 14-14: overly broad permissions (excessive-permissions): packages: write is overly broad at the workflow level
(excessive-permissions)
[error] 15-15: overly broad permissions (excessive-permissions): attestations: write is overly broad at the workflow level
(excessive-permissions)
[error] 16-16: overly broad permissions (excessive-permissions): id-token: write is overly broad at the workflow level
(excessive-permissions)
[error] 3-10: use of fundamentally insecure workflow trigger (dangerous-triggers): workflow_run is almost always used insecurely
(dangerous-triggers)
[error] 55-55: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 61-61: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 64-64: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 72-72: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 88-88: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[warning] 14-14: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
.github/workflows/deploy-production.yml
[error] 86-86: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 30-30: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[warning] 12-12: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
.github/workflows/ci.yml
[error] 37-37: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 43-43: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 242-242: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 247-247: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 253-253: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 275-275: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 280-280: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 286-286: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 299-299: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 304-304: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 310-310: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 323-323: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 378-378: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🔇 Additional comments (28)
infrastructure/deployment/scripts/prepare-host.sh (1)
4-56: LGTM!infrastructure/deployment/scripts/bootstrap-openfga.sh (1)
62-79: 🗄️ Data Integrity & IntegrationNo change needed for this parsing path.
The OpenFGA bootstrap script refuses a second store only if identifiers already exist in the environment file; any parse failure before persisting both IDs still leaves no persisted IDs, so retrying does not create another store under this guard.
> Likely an incorrect or invalid review comment.infrastructure/deployment/compose.production.yaml (2)
1-438: LGTM! The rest of the stack (network segmentation, resource limits, security hardening viacap_drop/no-new-privileges/read_only, dependency ordering viacondition: service_completed_successfully/service_healthy) is consistent and well constructed.
267-276: 🩺 Stability & AvailabilityHealthcheck can use
/dev/tcphere.The pinned Keycloak image uses UBI-based shell support and the inherited
CMD-SHELLpath supports this Bash redirect pattern in this image.> Likely an incorrect or invalid review comment.infrastructure/deployment/production.env.example (1)
1-68: LGTM! Overall the file is well-organized, uses:?-style required placeholders correctly, and documents secret-generation guidance.apps/api/src/main/resources/application-prod.yml (2)
2-24: LGTM! Datasource/Hikari tuning, forward-headers-strategy, graceful shutdown, and logging-level externalization all look correct.Also applies to: 72-82
66-71: 🔒 Security & PrivacyNo change needed; actuator health details are already authorized.
show-details: alwaysis matched by the actuator health permission, so the endpoint is only exposing details to authenticated users rather than permitting unauthenticated access to/actuator/health.apps/mcp/src/main/resources/application-prod.yml (1)
1-35: LGTM!apps/worker/src/main/resources/application-prod.yml (1)
2-15: LGTM! Datasource/Hikari and logging-level externalization are consistent with api's pattern.Also applies to: 42-50
infrastructure/postgres-rag/bootstrap-database.sql (1)
1-55: LGTM! The dynamic-SQL quoting viaformat()+ psql variable interpolation is the correct, safe pattern, and the create/alter split makes the script properly idempotent.infrastructure/postgres-rag/bootstrap-shared-databases.sh (1)
1-75: LGTM! Grants are appropriately least-privilege and consistent with the AGE runtime-role setup mirrored inApacheAgeGraphTopologyProjectionIntegrationTests.java.infrastructure/postgres-rag/backup-shared-postgres.sh (1)
1-49: LGTM! Existence checks and quoting are handled correctly, and the SHA256SUMS manifest is a nice integrity touch.integrations/graph-rag-postgres/src/test/java/com/orgmemory/graphrag/postgres/ApacheAgeGraphTopologyProjectionIntegrationTests.java (2)
65-88: LGTM! The admin/runtime role split correctly mirrors the least-privilege grants inbootstrap-shared-databases.sh, and the flagged static-analysis SQL-injection warnings on Lines 68-79 are false positives —RUNTIME_USER/RUNTIME_PASSWORDare hardcoded test constants, not untrusted input, and DDL statements likeCREATE ROLE/GRANT/ALTER ROLEcan't be parameterized via JDBC placeholders regardless.
50-53: 🗄️ Data Integrity & IntegrationNo change needed for the RC PostgreSQL RAG image default.
The
pg18-age1.8.0-rc0-pgvector0.8.4default matches the documented production Apache AGE pin for PostgreSQL 18 (PG18/v1.8.0-rc0), and the productionPOSTGRES_IMAGEis a separate pinned artifact.> Likely an incorrect or invalid review comment..dockerignore (1)
1-78: LGTM!.github/workflows/ci.yml (1)
21-434: LGTM!infrastructure/images/spring-boot-app.Dockerfile (1)
28-56: Solid non-root, multi-stage image.Non-root user, jar-layer extraction, and
APP_NAMEvalidation are well done. Note: no Docker-nativeHEALTHCHECKhere — flagged in a consolidated comment together with the Keycloak and Postgres-RAG images.infrastructure/keycloak/Dockerfile (1)
23-34: 🔒 Security & PrivacyVerify realm-import placeholder substitution actually resolves the client secret.
The imported realm JSON uses
${ORGMEMORY_OIDC_CLIENT_SECRET}for the client secret. Official Keycloak docs state that startup--import-realmis able to use placeholders to resolve values from environment variables for any realm configuration... the value set to the MY_REALM_NAME environment variable is going to be used to set the realm property. However, this exact feature has had reported regressions where the variable... should be replaced with the corresponding envvar value. The substitution is not processed, and the value is stored as a literal in some versions/contexts.Given this directly gates production OIDC login, confirm via a smoke test (post-deploy) that the client secret is actually substituted and not left as the literal placeholder string.
infrastructure/keycloak/orgmemory-realm.prod.json (1)
1-61: LGTM!infrastructure/postgres-rag/Dockerfile (2)
3-4: 🩺 Stability & AvailabilityConfirm the pinned Apache AGE commit corresponds to an intentional pre-release for production.
See consolidated comment (shared with
compose.yaml) regarding the-rc0AGE version being used in production.
29-44: Extension copy and bootstrap wiring look correct.Wildcard
age*copy and new bootstrap/backup script wiring are reasonable; HEALTHCHECK gap addressed in a consolidated comment with the other two Dockerfiles.web/Dockerfile (1)
1-39: LGTM!web/.oxlintrc.json (1)
5-5: LGTM!web/nginx.conf (2)
6-14: 🔒 Security & PrivacyConfirm this container isn't directly internet-reachable before trusting forwarded headers.
$forwarded_proto/$forwarded_portpass through client-suppliedX-Forwarded-Proto/X-Forwarded-Portunchanged when present. This is safe only if a trusted edge proxy (not shown in this batch) sits in front and always overwrites these headers before requests reach this container — otherwise a client could spoof them.
16-100: Routing correctly mirrors Spring Security OAuth2/MCP conventions.
/api/,/oauth2|login/,/error, and the exact-match/mcpstreaming route are all wired consistently with the backend's expected paths.compose.yaml (2)
1-3: LGTM!
15-15: 🩺 Stability & AvailabilityConfirm the
-rc0AGE tag is intentional for production.See consolidated comment (shared with
infrastructure/postgres-rag/Dockerfile) regarding using a release-candidate build of Apache AGE.Also applies to: 61-61
.github/workflows/build-images.yml (1)
3-10: 🔒 Security & PrivacyNo
workflow_runtrigger issue to raise.
| concurrency: | ||
| group: orgmemory-production | ||
| cancel-in-progress: false |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Enable production-workflow cancellation.
cancel-in-progress: false queues duplicate production deployments instead of cancelling superseded runs. Set it to true while preserving remote deployment locking. As per path instructions, GitHub Actions require “concurrency cancellation.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-production.yml around lines 16 - 18, Update the
production workflow’s concurrency configuration by changing cancel-in-progress
to true, while preserving the existing orgmemory-production group and remote
deployment locking behavior.
Source: Path instructions
| endpoint: | ||
| health: | ||
| show-details: ${ORGMEMORY_HEALTH_SHOW_DETAILS:always} | ||
| endpoints: |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
management.endpoint.health.probes.enabled isn't set here, unlike api/mcp.
Without probes.enabled: true, the /actuator/health/readiness and /liveness groups used by api/mcp's Docker healthchecks aren't available for worker. This pairs with the missing healthcheck: block for worker in infrastructure/deployment/compose.production.yaml (Lines 330-361) — enabling probes here is a prerequisite for wiring that healthcheck.
🛠️ Suggested fix
management:
endpoint:
health:
+ probes:
+ enabled: true
show-details: ${ORGMEMORY_HEALTH_SHOW_DETAILS:always}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| endpoint: | |
| health: | |
| show-details: ${ORGMEMORY_HEALTH_SHOW_DETAILS:always} | |
| endpoints: | |
| endpoint: | |
| health: | |
| probes: | |
| enabled: true | |
| show-details: ${ORGMEMORY_HEALTH_SHOW_DETAILS:always} | |
| endpoints: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/worker/src/main/resources/application-prod.yml` around lines 38 - 41,
Enable health probes in the worker’s management health configuration by adding
management.endpoint.health.probes.enabled: true alongside the existing health
settings in application-prod.yml, so the readiness and liveness actuator groups
are available for the worker healthcheck.
| worker: | ||
| <<: *service-defaults | ||
| image: ${ORGMEMORY_WORKER_IMAGE:?Set ORGMEMORY_WORKER_IMAGE} | ||
| environment: | ||
| <<: *spring-environment | ||
| ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE: ${ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE:-8} | ||
| ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE: ${ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE:-1} | ||
| ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY: ${ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY:-2} | ||
| JAVA_TOOL_OPTIONS: >- | ||
| -XX:InitialRAMPercentage=15 | ||
| -XX:MaxRAMPercentage=60 | ||
| -XX:+ExitOnOutOfMemoryError | ||
| -Dfile.encoding=UTF-8 | ||
| depends_on: | ||
| api: | ||
| condition: service_healthy | ||
| networks: | ||
| - orgmemory-internal | ||
| - shared-infra | ||
| read_only: true | ||
| tmpfs: | ||
| - /tmp:size=256m,mode=1777 | ||
| stop_grace_period: 45s | ||
| cpu_shares: 512 | ||
| deploy: | ||
| resources: | ||
| limits: | ||
| cpus: "1.50" | ||
| memory: 3g | ||
| reservations: | ||
| memory: 768m | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
worker has no healthcheck, unlike api and mcp.
api (Lines 307-316) and mcp (Lines 379-388) both define a wget-based actuator healthcheck, but worker has none despite its application-prod.yml enabling management.endpoints.web.exposure.include: health,info. Without a healthcheck, Docker cannot detect a hung/degraded worker and cannot restart it, and no other service can depends_on: worker: condition: service_healthy.
🛠️ Suggested fix
worker:
<<: *service-defaults
image: ${ORGMEMORY_WORKER_IMAGE:?Set ORGMEMORY_WORKER_IMAGE}
environment:
<<: *spring-environment
ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE: ${ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE:-8}
ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE: ${ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE:-1}
ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY: ${ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY:-2}
JAVA_TOOL_OPTIONS: >-
-XX:InitialRAMPercentage=15
-XX:MaxRAMPercentage=60
-XX:+ExitOnOutOfMemoryError
-Dfile.encoding=UTF-8
depends_on:
api:
condition: service_healthy
networks:
- orgmemory-internal
- shared-infra
+ healthcheck:
+ test:
+ - CMD-SHELL
+ - >-
+ wget -q -O - http://127.0.0.1:8080/actuator/health/readiness
+ | grep -q '"status":"UP"'
+ interval: 10s
+ timeout: 5s
+ retries: 18
+ start_period: 45s
read_only: true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| worker: | |
| <<: *service-defaults | |
| image: ${ORGMEMORY_WORKER_IMAGE:?Set ORGMEMORY_WORKER_IMAGE} | |
| environment: | |
| <<: *spring-environment | |
| ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE: ${ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE:-8} | |
| ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE: ${ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE:-1} | |
| ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY: ${ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY:-2} | |
| JAVA_TOOL_OPTIONS: >- | |
| -XX:InitialRAMPercentage=15 | |
| -XX:MaxRAMPercentage=60 | |
| -XX:+ExitOnOutOfMemoryError | |
| -Dfile.encoding=UTF-8 | |
| depends_on: | |
| api: | |
| condition: service_healthy | |
| networks: | |
| - orgmemory-internal | |
| - shared-infra | |
| read_only: true | |
| tmpfs: | |
| - /tmp:size=256m,mode=1777 | |
| stop_grace_period: 45s | |
| cpu_shares: 512 | |
| deploy: | |
| resources: | |
| limits: | |
| cpus: "1.50" | |
| memory: 3g | |
| reservations: | |
| memory: 768m | |
| worker: | |
| <<: *service-defaults | |
| image: ${ORGMEMORY_WORKER_IMAGE:?Set ORGMEMORY_WORKER_IMAGE} | |
| environment: | |
| <<: *spring-environment | |
| ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE: ${ORGMEMORY_WORKER_DB_POOL_MAXIMUM_SIZE:-8} | |
| ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE: ${ORGMEMORY_WORKER_DB_POOL_MINIMUM_IDLE:-1} | |
| ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY: ${ORGMEMORY_GRAPH_EXTRACTION_CONCURRENCY:-2} | |
| JAVA_TOOL_OPTIONS: >- | |
| -XX:InitialRAMPercentage=15 | |
| -XX:MaxRAMPercentage=60 | |
| -XX:+ExitOnOutOfMemoryError | |
| -Dfile.encoding=UTF-8 | |
| depends_on: | |
| api: | |
| condition: service_healthy | |
| networks: | |
| - orgmemory-internal | |
| - shared-infra | |
| healthcheck: | |
| test: | |
| - CMD-SHELL | |
| - >- | |
| wget -q -O - http://127.0.0.1:8080/actuator/health/readiness | |
| | grep -q '"status":"UP"' | |
| interval: 10s | |
| timeout: 5s | |
| retries: 18 | |
| start_period: 45s | |
| read_only: true | |
| tmpfs: | |
| - /tmp:size=256m,mode=1777 | |
| stop_grace_period: 45s | |
| cpu_shares: 512 | |
| deploy: | |
| resources: | |
| limits: | |
| cpus: "1.50" | |
| memory: 3g | |
| reservations: | |
| memory: 768m |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/deployment/compose.production.yaml` around lines 330 - 361,
Add a wget-based healthcheck to the worker service, matching the existing
actuator healthcheck pattern used by api and mcp and targeting the worker’s
exposed health endpoint. Keep the check’s timing, timeout, retries, and start
period consistent with those services.
| OPENFGA_DB_USER=openfga | ||
| OPENFGA_DB_PASSWORD= | ||
| # URL-encode special characters in the password. | ||
| OPENFGA_DATASTORE_URI=postgres://openfga:<url-encoded-password>@postgres:5432/openfga?sslmode=disable |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
sslmode=disable explicitly disables TLS for the OpenFGA→Postgres connection.
This is the only connection string in the file that explicitly disables TLS; the other JDBC URLs (ORGMEMORY_DB_URL, KC_DB_URL) don't specify sslmode at all. If the shared Postgres host is reachable over anything other than a fully trusted, isolated network, this is a plaintext-credentials-in-transit risk. Consider sslmode=require or verify-full for consistency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/deployment/production.env.example` at line 26, Update the
OPENFGA_DATASTORE_URI example to use an explicit TLS-enforcing sslmode,
preferably verify-full or the project’s established secure equivalent, instead
of sslmode=disable; preserve the existing host, database, and password
placeholders.
| # POC diagnostics. Set ORGMEMORY_APP_LOG_LEVEL=INFO after the pilot. | ||
| ORGMEMORY_ROOT_LOG_LEVEL=INFO | ||
| ORGMEMORY_APP_LOG_LEVEL=DEBUG | ||
| ORGMEMORY_SECURITY_LOG_LEVEL=INFO | ||
| ORGMEMORY_SQL_LOG_LEVEL=INFO | ||
| ORGMEMORY_HEALTH_SHOW_DETAILS=always |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Default ORGMEMORY_APP_LOG_LEVEL=DEBUG risks logging sensitive data in production.
The comment flags this as a temporary "POC" setting, but it ships as the default in the example env file consumed directly by compose.production.yaml's spring-environment anchor and every application-prod.yml. DEBUG on com.orgmemory (the whole application package) can capture request/response payloads, tokens, or other PII in logs. Worth a stronger safeguard than a comment — e.g., default to INFO and require an explicit opt-in for DEBUG, or scope DEBUG to specific non-sensitive packages only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@infrastructure/deployment/production.env.example` around lines 53 - 58,
Change ORGMEMORY_APP_LOG_LEVEL in the production environment defaults from DEBUG
to INFO, keeping production logging at the safer level by default. Require an
explicit operator override for DEBUG rather than relying only on the POC
comment, and leave the other logging settings unchanged.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/build-images.yml (1)
1-21: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winEnable concurrency cancellation.
cancel-in-progress: falseallows duplicate runs for the same commit to continue concurrently. The repository path instructions require concurrency cancellation; set this totrueto avoid redundant production builds and pushes.Proposed fix
- cancel-in-progress: false + cancel-in-progress: true🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/build-images.yml around lines 1 - 21, Update the workflow’s concurrency configuration to set cancel-in-progress to true, ensuring duplicate production image builds for the same commit are cancelled while preserving the existing concurrency group.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build-images.yml:
- Around line 85-94: Update the “Scan local image” Trivy action step to fail the
matrix job on CRITICAL/HIGH findings by removing continue-on-error and
configuring exit-code to a nonzero failure value. Preserve the existing image
reference, severity filters, and publication flow so the job stops before Line
95 when vulnerabilities are detected.
- Around line 95-110: Update the publish flow around the “Publish immutable
verified image” build step so it publishes the exact image artifact produced and
scanned by the earlier scan step, rather than rebuilding it independently. Reuse
the scanned image or its digest, retagging it as the sha-${{
github.event.workflow_run.head_sha }} image before signing or attaching
attestations; ensure the published digest remains identical to the scanned
digest.
---
Outside diff comments:
In @.github/workflows/build-images.yml:
- Around line 1-21: Update the workflow’s concurrency configuration to set
cancel-in-progress to true, ensuring duplicate production image builds for the
same commit are cancelled while preserving the existing concurrency group.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7b73bef6-0da3-4def-81c0-a4d3234d6d8b
⛔ Files ignored due to path filters (2)
docs/increments/active/2026-07-25-production-cicd-zm/design.mdis excluded by!docs/**docs/runbooks/production-zm-deployment.mdis excluded by!docs/**
📒 Files selected for processing (5)
.github/workflows/build-images.ymlinfrastructure/deployment/compose.production.yamlinfrastructure/deployment/scripts/deploy.shinfrastructure/deployment/scripts/smoke-production.shinfrastructure/postgres-rag/backup-shared-postgres.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Web · Node 24
- GitHub Check: PostgreSQL GraphRAG
- GitHub Check: Backend · Java 25
🧰 Additional context used
📓 Path-based instructions (3)
.github/**/*.{yml,yaml}
⚙️ CodeRabbit configuration file
.github/**/*.{yml,yaml}: Require least-privilege permissions, explicit release tags for actions,
bounded job timeouts, concurrency cancellation, frozen lockfiles, and no
secrets in pull-request workflows. GitHub Actions are intentionally not
pinned to commit SHAs; Dependabot owns their scheduled version updates.
Files:
.github/workflows/build-images.yml
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
**/*: Before changing unfamiliar Spring Boot 4, Spring Modulith 2, Spring AI 2, Gradle, React, Vite, Tailwind, or TypeScript APIs, consult Context7/current official documentation and the projectorgmemory-*verification skills.
Readdocs/guidelines/agent-safety.mdbefore retrieval, AI, MCP, permission, upload, graph, or export work.
Never commit.envfiles, provider keys, tokens, or customer data.
Run the relevant gates fromdocs/guidelines/testing-harness.md; use a terminating clean test as the context gate, and do not treatbootRunas verification.
Current behavior belongs in architecture/specs only after it exists in code; intent belongs in vision, roadmap, or an active increment, and repository state must not be duplicated across documents.
Files:
infrastructure/postgres-rag/backup-shared-postgres.shinfrastructure/deployment/scripts/smoke-production.shinfrastructure/deployment/scripts/deploy.shinfrastructure/deployment/compose.production.yaml
**/*.{yml,yaml,properties}
📄 CodeRabbit inference engine (CLAUDE.md)
Keep
ddl-auto=validatein application configuration.
Files:
infrastructure/deployment/compose.production.yaml
🧠 Learnings (1)
📚 Learning: 2026-07-24T22:52:57.466Z
Learnt from: kl3inIT
Repo: kl3inIT/OrgMemory PR: 40
File: .github/workflows/ci.yml:126-126
Timestamp: 2026-07-24T22:52:57.466Z
Learning: In this repository’s GitHub Actions workflows, the `uses:` field may intentionally reference GitHub Actions by explicit release tags (not immutable commit SHAs) per the project’s OrgMemory policy. Do not flag tag-based `uses:` references as “unpinned” if they are release-tag-based (e.g., `owner/repovX.Y.Z`) and follow the repo’s Dependabot-owned scheduled updates approach.
Applied to files:
.github/workflows/build-images.yml
🪛 zizmor (1.26.1)
.github/workflows/build-images.yml
[error] 71-71: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 87-87: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 97-97: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🔇 Additional comments (5)
infrastructure/deployment/compose.production.yaml (1)
16-21: LGTM!Also applies to: 178-178, 369-369
infrastructure/postgres-rag/backup-shared-postgres.sh (1)
9-11: LGTM!infrastructure/deployment/scripts/deploy.sh (1)
150-154: LGTM!infrastructure/deployment/scripts/smoke-production.sh (1)
14-60: LGTM!.github/workflows/build-images.yml (1)
70-84: LGTM!
| - name: Scan local image | ||
| continue-on-error: true | ||
| uses: aquasecurity/trivy-action@v0.36.0 | ||
| with: | ||
| image-ref: orgmemory-${{ matrix.name }}:scan-${{ github.event.workflow_run.head_sha }} | ||
| format: table | ||
| ignore-unfixed: true | ||
| severity: CRITICAL,HIGH | ||
| exit-code: "0" | ||
|
|
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major
Make Trivy findings block publication.
continue-on-error: true and exit-code: "0" ensure that CRITICAL/HIGH findings never fail the job, so Line 95 still publishes the image. Make the scan fail the matrix job before publication.
Proposed fix
- continue-on-error: true
uses: aquasecurity/trivy-action@v0.36.0
@@
- exit-code: "0"
+ exit-code: "1"🧰 Tools
🪛 zizmor (1.26.1)
[error] 87-87: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/build-images.yml around lines 85 - 94, Update the “Scan
local image” Trivy action step to fail the matrix job on CRITICAL/HIGH findings
by removing continue-on-error and configuring exit-code to a nonzero failure
value. Preserve the existing image reference, severity filters, and publication
flow so the job stops before Line 95 when vulnerabilities are detected.
Outcome
om.kl3in.techandauth.kl3in.techCI GateSecurity and operations
productionenvironment uses pinned SSH host verificationVerification
./gradlew --no-daemon clean buildexcluding independent datastore adapter suitesSummary by CodeRabbit