Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,24 @@ RABBITMQ_PORT=5672
RABBITMQ_HOST_PORT=5672
RABBITMQ_MANAGEMENT_PORT=15672
RABBITMQ_MANAGEMENT_HOST_PORT=15672
RABBITMQ_STREAM_PORT=5552
RABBITMQ_STREAM_HOST_PORT=5552
RABBITMQ_DEFAULT_USER=coredata
RABBITMQ_DEFAULT_PASS_FILE=./secrets/rabbitmq_default_pass
RABBITMQ_ERLANG_COOKIE_FILE=./secrets/rabbitmq_erlang_cookie
RABBITMQ_DATA_MOUNT_PATH=/var/lib/rabbitmq
# Pre-built RabbitMQ image uses UID 100, GID 101 (the rabbitmq user baked into the image).
RABBITMQ_UID=100
RABBITMQ_GID=101
# Container resource limits (0 = unlimited).
RABBITMQ_MEMORY_LIMIT=0
RABBITMQ_CPU_LIMIT=0.0
# Erlang VM tuning flags passed via RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS.
# +sbwt none — disable speculative scheduler busy-waiting (saves CPU)
# +sbwtdcpu none — disable dirty-CPU scheduler busy-waiting
# +sbwtdio none — disable dirty-IO scheduler busy-waiting
# +stbt ts — bind scheduler threads to topology (reduces context switches)
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="+sbwt none +sbwtdcpu none +sbwtdio none +stbt ts"

# Time zone for containers
TZ=UTC
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,19 @@ services:
TZ: ${TZ}
entrypoint: ["/opt/core_data/bin/rabbitmq-entrypoint.sh"]
command: ["rabbitmq-server"]
mem_limit: ${RABBITMQ_MEMORY_LIMIT:-0}
cpus: ${RABBITMQ_CPU_LIMIT:-0.0}
volumes:
- ./data/rabbitmq_data:${RABBITMQ_DATA_MOUNT_PATH:-/var/lib/rabbitmq}
- ./rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
- ./secrets/rabbitmq_default_pass:/run/secrets/rabbitmq_default_pass:ro
- ./secrets/rabbitmq_erlang_cookie:/run/secrets/rabbitmq_erlang_cookie:ro
networks:
- core_data
ports:
- "${RABBITMQ_HOST_PORT}:${RABBITMQ_PORT}"
- "${RABBITMQ_MANAGEMENT_HOST_PORT}:${RABBITMQ_MANAGEMENT_PORT}"
- "${RABBITMQ_STREAM_HOST_PORT:-5552}:${RABBITMQ_STREAM_PORT:-5552}"
healthcheck:
test: ["CMD-SHELL", "/opt/core_data/bin/rabbitmq-healthcheck.sh"]
interval: 10s
Expand Down
26 changes: 25 additions & 1 deletion docker/rabbitmq/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,31 @@
FROM rabbitmq:4.2-management-alpine

LABEL org.opencontainers.image.source="https://github.com/paudley/core_data" \
org.opencontainers.image.description="Core Data RabbitMQ with hardened entrypoint"
org.opencontainers.image.description="Core Data RabbitMQ with hardened entrypoint and core plugins"

# Enable tier-1 core plugins required by the cognitive architecture and
# production operations tooling. The management image already enables
# rabbitmq_management, rabbitmq_management_agent, rabbitmq_web_dispatch,
# and rabbitmq_prometheus.
#
# Core requirements:
# consistent_hash_exchange – session affinity for LLM prefix-cache
# stream / stream_management – append-only Cognitive_Stream, Dreaming offsets
#
# Production operations:
# shovel / shovel_management – queue draining, dead-letter reprocessing
# event_exchange – broker events as AMQP messages (observability)
# tracing – message-level tracing via management UI
# top – per-process resource monitoring
RUN rabbitmq-plugins enable --offline \
rabbitmq_consistent_hash_exchange \
rabbitmq_stream \
rabbitmq_stream_management \
rabbitmq_shovel \
rabbitmq_shovel_management \
rabbitmq_event_exchange \
rabbitmq_tracing \
rabbitmq_top

# Add secrets group and add rabbitmq user to it for reading shared secrets
ARG SECRETS_GID=65532
Expand Down
92 changes: 92 additions & 0 deletions rabbitmq/rabbitmq.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SPDX-FileCopyrightText: 2025 Blackcat Informatics® Inc.
# SPDX-License-Identifier: MIT
#
# Core Data — RabbitMQ Production Configuration
#
# This file is mounted read-only into the container at
# /etc/rabbitmq/rabbitmq.conf. Edit values here directly;
# RabbitMQ's sysctl-format config does not support environment
# variable substitution.
#
# Reference: https://www.rabbitmq.com/docs/configure
# Reference: https://www.rabbitmq.com/docs/production-checklist

# ──────────────────────────────────────────────
# Memory & flow control
# ──────────────────────────────────────────────

# In containerized environments use an absolute limit rather than a
# relative watermark so the Erlang VM does not mis-detect available
# memory. Adjust to match your container's mem_limit.
# 512 MiB is suitable for a lightweight single-node broker.
vm_memory_high_watermark.absolute = 512MiB

# Begin paging messages to disk when memory reaches 50 % of the
# watermark. At 512 MiB this triggers at ~256 MiB.
vm_memory_high_watermark_paging_ratio = 0.5

# Use Erlang allocator stats for memory calculation — more accurate
# than the default RSS-based strategy inside containers.
vm_memory_calculation_strategy = allocated

# ──────────────────────────────────────────────
# Disk free space
# ──────────────────────────────────────────────

# Minimum free disk before the broker blocks publishers. Should
# roughly match the memory watermark to ensure safe page-out.
disk_free_limit.absolute = 512MiB

# ──────────────────────────────────────────────
# Networking & connections
# ──────────────────────────────────────────────

# Heartbeat — detect dead TCP connections. 60 s is a safe default
# that avoids false positives under transient load.
heartbeat = 60

# Maximum channels per connection. Prevents a single client from
# exhausting broker resources.
channel_max = 128

# TCP listen backlog — how many pending connections the kernel queues
# before refusing new ones.
tcp_listen_options.backlog = 256

# Enable TCP keepalives so the OS detects half-open connections.
tcp_listen_options.keepalive = true

# Disable Nagle's algorithm for lower message latency.
tcp_listen_options.nodelay = true

# ──────────────────────────────────────────────
# Queue & message defaults
# ──────────────────────────────────────────────

# Default queue type for new declarations that do not specify one.
# Classic queues are the correct choice for a single-node deployment
# (quorum queues add Raft overhead with no replication benefit).
default_queue_type = classic

# Consumer delivery acknowledgement timeout (ms). Consumers that
# hold messages longer than 30 minutes without ack are disconnected.
consumer_timeout = 1800000

# ──────────────────────────────────────────────
# Management & monitoring
# ──────────────────────────────────────────────

# Increase the statistics emission interval from 5 s to 15 s.
# Reduces periodic overhead on connections, channels and queues
# while remaining sufficient for Prometheus scrape intervals.
collect_statistics_interval = 15000

# Disable guest user login from remote hosts (security hardening).
loopback_users.guest = true

# ──────────────────────────────────────────────
# Streams
# ──────────────────────────────────────────────

# Stream protocol listener port.
stream.listeners.tcp.1 = 5552
2 changes: 1 addition & 1 deletion scripts/lib/maintenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ cmd_pgbadger_report() {
local cmd=(pgbadger --quiet --format csv --jobs "$jobs" --outfile "$output")
[[ -n $since ]] && cmd+=(--begin "$since")
cmd+=(/var/lib/postgresql/data/log/postgresql-*.csv)
compose_exec bash -lc "${cmd[@]}"
compose_exec bash -lc "${cmd[*]}"
echo "pgBadger report written to ${output}" >&2
Comment on lines 56 to 60

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

compose_exec bash -lc "${cmd[*]}" builds a shell command string from user-controlled values (--since, --output) without robust escaping. This can break when values contain spaces/shell metacharacters and can lead to shell injection inside the container. Prefer executing pgbadger without bash -lc (if possible), or escape each argument (e.g., via printf %q) / pass args via bash -lc '... "$@"' to avoid interpolation.

Copilot uses AI. Check for mistakes.
}

Expand Down
7 changes: 7 additions & 0 deletions scripts/lib/rabbitmq.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ RABBITMQ_SERVICE_NAME=${RABBITMQ_SERVICE_NAME:-rabbitmq}
RABBITMQ_HOST=${RABBITMQ_HOST:-rabbitmq}
RABBITMQ_PORT=${RABBITMQ_PORT:-5672}
RABBITMQ_MANAGEMENT_PORT=${RABBITMQ_MANAGEMENT_PORT:-15672}
RABBITMQ_STREAM_PORT=${RABBITMQ_STREAM_PORT:-5552}

ensure_rabbitmq_service() {
if ! compose_has_service "${RABBITMQ_SERVICE_NAME}"; then
Expand Down Expand Up @@ -86,6 +87,12 @@ USAGE
echo "[rabbitmq] Definitions written to ${output_path}" >&2
}

cmd_rabbitmq_plugins() {
ensure_env
ensure_rabbitmq_service
rabbitmq_exec rabbitmq-plugins list "$@"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation hardcodes the list subcommand, which prevents using other rabbitmq-plugins commands like enable or disable. This contradicts the help text in manage.sh which states "List or manage RabbitMQ plugins." For example, running manage.sh rabbitmq-plugins enable my_plugin would incorrectly execute rabbitmq-plugins list enable my_plugin.

To align with the documented behavior and the test plan (which requires manage.sh rabbitmq-plugins to default to list), I suggest checking if arguments are provided. If not, default to list; otherwise, pass all arguments through to rabbitmq-plugins.

Suggested change
rabbitmq_exec rabbitmq-plugins list "$@"
if [[ $# -eq 0 ]]; then
rabbitmq_exec rabbitmq-plugins list
else
rabbitmq_exec rabbitmq-plugins "$@"
fi

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

cmd_rabbitmq_plugins always runs rabbitmq-plugins list and then appends user-supplied args. This prevents using other subcommands (enable/disable/etc.) and also breaks common usage like manage.sh rabbitmq-plugins list (it becomes rabbitmq-plugins list list). Consider passing through to rabbitmq-plugins directly, or defaulting to list only when no args are provided.

Suggested change
rabbitmq_exec rabbitmq-plugins list "$@"
if [[ $# -eq 0 ]]; then
rabbitmq_exec rabbitmq-plugins list
else
rabbitmq_exec rabbitmq-plugins "$@"
fi

Copilot uses AI. Check for mistakes.
}

cmd_rabbitmq_overview() {
ensure_env
ensure_rabbitmq_service
Expand Down
4 changes: 4 additions & 0 deletions scripts/manage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ Cache, messaging, pooling
rabbitmq-ctl [args] Run rabbitmqctl inside the RabbitMQ container.
rabbitmq-diagnostics [args] Run rabbitmq-diagnostics inside RabbitMQ.
rabbitmq-export [--output PATH] Export RabbitMQ definitions to host (JSON).
rabbitmq-plugins [args] List or manage RabbitMQ plugins.
rabbitmq-overview Show rabbitmq-diagnostics status summary.
pgbouncer-stats SHOW STATS via PgBouncer admin console.
pgbouncer-pools SHOW POOLS via PgBouncer admin console.
Expand Down Expand Up @@ -1507,6 +1508,9 @@ rabbitmq-diagnostics)
rabbitmq-export)
cmd_rabbitmq_export "$@"
;;
rabbitmq-plugins)
cmd_rabbitmq_plugins "$@"
;;
Comment on lines +1511 to +1513

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

A new rabbitmq-plugins manage.sh command is introduced here but isn’t covered by the existing tests/test_manage.py workflow suite that exercises other manage.sh commands. Adding a test that runs manage.sh rabbitmq-plugins under the rabbitmq profile (and asserts expected plugin names show up) would prevent regressions (and would have caught issues like list list).

Copilot uses AI. Check for mistakes.
rabbitmq-overview)
cmd_rabbitmq_overview "$@"
;;
Expand Down
Loading