fix(infra): unify production Docker DNS - #329
Conversation
📝 WalkthroughWalkthroughProduction, documentation, and observability Compose stacks now share an external Docker network with stable service aliases. A Python verifier checks network, DNS, and port contracts, and CI runs it during deployment validation. ChangesShared Docker DNS topology
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CI as ci.yml
participant Verifier as verify-shared-dns-network.py
participant ComposeStacks as Compose stacks
participant SharedInfra as shared-infra
CI->>Verifier: Run deployment topology validation
Verifier->>ComposeStacks: Render production, observability, and docs configurations
ComposeStacks->>SharedInfra: Resolve shared network name and memberships
Verifier-->>CI: Return validation status and failures
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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: 3
🤖 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 `@ARCHITECTURE.md`:
- Around line 680-685: Replace universal shared-infra membership with scoped
external networks for each permitted flow, keeping product and
observability-private networks intact and excluding node-exporter and privileged
cAdvisor unless explicitly required. Apply the network and service-membership
updates in ARCHITECTURE.md, infrastructure/deployment/compose.docs.yaml (lines
11-13), infrastructure/deployment/compose.production.yaml (lines 421-424),
infrastructure/observability/compose.observability.yaml (lines 185-187 and
205-207), and update
infrastructure/deployment/scripts/verify-shared-dns-network.py (lines 192-196)
allowlists to enforce those scoped memberships. Update
.tegami/2026-08-06-shared-service-dns.md (lines 9-12) to document the new
per-flow networks.
In `@infrastructure/deployment/scripts/verify-shared-dns-network.py`:
- Around line 198-204: Update the port comparison in the verification flow
around EXPECTED_PORTS and actual_ports so both rendered Compose ports and
expectations use the same canonical mapping schema. Normalize EXPECTED_PORTS
entries to the host/container/protocol mapping fields returned by docker compose
config, or canonicalize both sides before comparing, while preserving the
existing failure reporting for genuine mismatches.
In `@infrastructure/observability/README.md`:
- Around line 15-22: The trace-check instructions should use the configured
external network name instead of hardcoding shared-infra. Update the command
around the trace check to load or derive the network value from
observability.env, specifically honoring ORGMEMORY_SHARED_INFRA_NETWORK, and
pass that value to the throwaway container’s network option.
🪄 Autofix
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: c44f61fd-9db1-428b-ad67-52060bef6ca1
⛔ Files ignored due to path filters (1)
docs/increments/active/2026-07-25-production-cicd-zm/design.mdis excluded by!docs/**
📒 Files selected for processing (8)
.github/workflows/ci.yml.tegami/2026-08-06-shared-service-dns.mdARCHITECTURE.mdinfrastructure/deployment/compose.docs.yamlinfrastructure/deployment/compose.production.yamlinfrastructure/deployment/scripts/verify-shared-dns-network.pyinfrastructure/observability/README.mdinfrastructure/observability/compose.observability.yaml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*
📄 CodeRabbit inference engine (AGENTS.md)
**/*: Treat the repository and runtime evidence as the engineering system of record; do not treat chat or Northstar as authoritative.
Before changing a domain, read its specification, test-coverage document, and applicable decision filenames.
Material decisions about domain boundaries, authorization, persistence, publication, concurrency, cache isolation, parity scope, or deployment require an independent architecture challenge and documented alternatives before implementation.
Do not use completed increment documents as the source for current behavior; use them only for history or archaeology.
Before using unfamiliar Spring Boot, Spring Modulith, Spring AI, Gradle, React, Vite, Tailwind, TypeScript, Next.js, or Fumadocs APIs, consult current official documentation, Context7, and the relevant verification skill.
Readdocs/guidelines/agent-safety.mdbefore retrieval, AI, MCP, permission, upload, graph, or export work; never commit secrets or customer data.
Keepddl-auto=validateand pair every persisted-model change with a Flyway migration.
Use the testing harness; a terminating clean test is the JVM context gate, andbootRunis not verification.
Files:
infrastructure/deployment/compose.docs.yamlinfrastructure/observability/README.mdARCHITECTURE.mdinfrastructure/observability/compose.observability.yamlinfrastructure/deployment/compose.production.yamlinfrastructure/deployment/scripts/verify-shared-dns-network.py
.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/ci.yml
🧠 Learnings (3)
📚 Learning: 2026-08-05T09:53:56.596Z
Learnt from: kl3inIT
Repo: kl3inIT/OrgMemory PR: 300
File: .tegami/2026-08-05-retrieval-recall-observations.md:7-7
Timestamp: 2026-08-05T09:53:56.596Z
Learning: For Tegami release fragments in `.tegami/*.md`, do not require a top-level H1 solely to satisfy markdownlint MD041. These fragments must instead contain an approved level-two public changelog section, such as `## Improvements`, as enforced by `scripts/check-release.mjs`; the generated release output contract prevents adding a second page-level H1.
Applied to files:
.tegami/2026-08-06-shared-service-dns.md
📚 Learning: 2026-07-27T14:53:53.633Z
Learnt from: kl3inIT
Repo: kl3inIT/OrgMemory PR: 92
File: infrastructure/deployment/compose.production.yaml:304-310
Timestamp: 2026-07-27T14:53:53.633Z
Learning: For OrgMemory’s Spring Boot SCIM configuration, the `application.yml`/`application-prod.yml` map `orgmemory.security.scim.*` properties via `${ORGMEMORY_SCIM_*}` placeholders. Therefore, in deployment Compose files and related environment/CI templates, set environment variables using the `ORGMEMORY_SCIM_*` names (e.g., `ORGMEMORY_SCIM_VERIFIER_KEY`) rather than “relaxed-binding-derived” names such as `ORGMEMORY_SECURITY_SCIM_*`. This is required to ensure Spring resolves the intended SCIM configuration properties.
Applied to files:
infrastructure/deployment/compose.docs.yamlinfrastructure/deployment/compose.production.yaml
📚 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/ci.yml
🪛 ast-grep (0.45.0)
infrastructure/deployment/scripts/verify-shared-dns-network.py
[error] 98-113: Command coming from incoming request
Context: subprocess.run(
[
"docker",
"compose",
"--file",
str(relative_path),
"config",
"--no-interpolate",
"--format",
"json",
],
cwd=REPOSITORY,
check=False,
capture_output=True,
text=True,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(subprocess-from-request)
🪛 markdownlint-cli2 (0.23.2)
.tegami/2026-08-06-shared-service-dns.md
[warning] 7-7: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
| Every production, documentation, and observability service joins that external | ||
| `shared-infra` network as an additive Docker DNS fabric. Compose-private networks | ||
| remain in place for product- and observability-internal routes, and only services | ||
| that already join the proxy network are browser-accessible. Shared DNS membership | ||
| does not publish a container port to the host; it lets operators and cross-stack | ||
| integrations use stable service names instead of container IP addresses. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repo files of interest =="
git ls-files | rg '(^ARCHITECTURE\.md$|infrastructure/observability/compose\.observability\.yaml|infrastructure/deployment/compose\.docs\.yaml|infrastructure/deployment/compose\.production\.yaml|infrastructure/deployment/scripts/verify-shared-dns-network\.py|\.tegami/2026-08-06-shared-service-dns\.md|docs/guidelines/agent-safety\.md|.*decision.*|.*architecture.*)' || true
echo
echo "== relevant sections =="
printf '\n--- ARCHITECTURE.md 660-700 ---\n'
sed -n '660,700p' ARCHITECTURE.md
printf '\n--- compose.docs.yaml ---\n'
cat -n infrastructure/deployment/compose.docs.yaml
printf '\n--- compose.production.yaml 390-435 ---\n'
sed -n '390,435p' infrastructure/deployment/compose.production.yaml
printf '\n--- compose.observability.yaml 150-220 ---\n'
sed -n '150,220p' infrastructure/observability/compose.observability.yaml
printf '\n--- verify-shared-dns-network.py 170-205 ---\n'
sed -n '170,205p' infrastructure/deployment/scripts/verify-shared-dns-network.py
printf '\n--- tegami note ---\n'
cat -n .tegami/2026-08-06-shared-service-dns.md
echo
echo "== network declarations and all shared-infra memberships =="
python3 - <<'PY'
import yaml, pathlib
for path in pathlib.Path('infrastructure').rglob('compose*.yaml'):
data=yaml.safe_load(path.read_text())
print(path)
if 'networks' in data:
print('networks:', data['networks'])
print('services joined to shared-infra:')
for svc, sdata in (data.get('services') or {}).items():
nets=sdata.get('networks')
if isinstance(nets, dict) and 'shared-infra' in nets:
print(f' {svc}: {nets}')
elif isinstance(nets, list) and 'shared-infra' in nets:
print(f' {svc}: {nets}')
PYRepository: kl3inIT/OrgMemory
Length of output: 15010
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== compose.observability.yaml full networks section and exporter services =="
python3 - <<'PY'
from pathlib import Path
p=Path('infrastructure/observability/compose.observability.yaml')
lines=p.read_text().splitlines()
for i,l in enumerate(lines,1):
if l.startswith('networks:') or i>=220:
start=max(1,i-4); end=min(len(lines),i+45)
print(f'\n--- lines {start}-{end} ---')
for n in range(start,end+1):
print(f'{n:4d}: {lines[n-1]}')
PY
echo
echo "== inspect verifier PRESERVED_NETWORK_MEMBERSHIPS and EXPECTED_PORTS =="
sed -n '1,170p' infrastructure/deployment/scripts/verify-shared-dns-network.py
echo
echo "== Docker network docs evidence by local availability =="
python3 - <<'PY'
from pathlib import Path
import urllib.request
url='https://docs.docker.com/engine/network/drivers/bridge/'
for timeout in [2,5]:
try:
with urllib.request.urlopen(url, timeout=timeout) as r:
text=r.read().decode('utf-8', errors='replace')
idx=text.find('Containers on the same user-defined bridge network can reach each other by container name or IP address')
print('url_ok=true')
print('found_direct_reach=', idx!=-1)
print('excerpt=', text[max(0,idx-120):idx+220] if idx!=-1 else 'NOT_FOUND')
except Exception as e:
print('url_ok=false error=', e)
PY
echo
echo "== search architecture/security docs for DNS/network trust boundaries =="
rg -n "shared-infra|sharing Docker network|Docker network|directly communicate|cross-stack|observability-internal|untrusted|trust boundary|allowlist" ARCHITECTURE.md docs apps .tegami 2>/dev/null || true
echo
echo "== locate referenced architecture challenge/session material =="
for f in ".agents/skills/orgmemory-architecture-challenge/references/session-evidence.md" ".claude/skills/orgmemory-architecture-challenge/SKILL.md" ".codex/skills/orgmemory-architecture-challenge/SKILL.md"; do
if [ -f "$f" ]; then
echo "--- $f ---"
wc -l "$f"
rg -n "shared-infra|Docker network|DNS|observability|decision alternatives|architecture challenge" "$f" || true
fi
doneRepository: kl3inIT/OrgMemory
Length of output: 50373
🌐 Web query:
Docker engine bridge driver containers user-defined bridge network container name IP address isolation internal
💡 Result:
Docker Engine provides two primary types of bridge networks, each with distinct behaviors regarding container communication, naming, and isolation [1][2][3]. Default Bridge Network When the Docker daemon starts, it creates a default bridge network named bridge [1][2]. Unless otherwise specified, all newly created containers are attached to this network [1][2]. - Communication: Containers on the default bridge can communicate with each other, but only by IP address [1][3]. - Name Resolution: Automatic DNS-based name resolution is not supported on the default bridge network [1][4]. Historically, the legacy --link flag was used to facilitate communication by name, but this is now deprecated [1][2]. - Isolation: This network lacks logical isolation; all containers connected to it are able to communicate unless restricted by host-level firewall rules [1][2]. User-Defined Bridge Networks Users can create custom bridge networks to gain superior networking capabilities [1][2]. - Automatic Service Discovery: User-defined bridge networks feature an embedded DNS server [3][5]. This allows containers to resolve each other by their container name or alias, making inter-container communication configuration significantly more stable and easier to manage [1][4]. - Isolation: User-defined bridge networks provide better network isolation [1][2]. By default, containers on different user-defined networks cannot communicate with each other unless specifically connected to multiple networks [2][6]. - Internal Networks: You can create a bridge network with the --internal flag [6]. This prevents the network from having external connectivity (e.g., to the internet) while still allowing communication between the containers on that network [6]. Technical Implementation Docker achieves this isolation and communication control through the use of Linux kernel features, primarily iptables (or nftables) and software bridges [2][7][8]. The Docker Engine automatically manages firewall rules to enforce these boundaries [7][8]. When using user-defined networks, the embedded DNS server resides at 127.0.0.11 within the container's network namespace to handle resolution requests [3][5]. Because these rules are critical for network functionality, it is recommended to avoid manual modification of the firewall rules created by the Docker Engine [7].
Citations:
- 1: https://docs.docker.com/engine/network/drivers/bridge/
- 2: https://web.archive.org/web/20200223114458/https:/docs.docker.com/network/bridge/
- 3: https://docs.docker.com/engine/network/
- 4: https://www.luisllamas.es/en/docker-user-defined-networks/
- 5: https://blog.devops.dev/how-dns-resolution-works-in-custom-docker-bridge-network-and-not-on-default-docker0-bridge-network-adab783cdb49
- 6: https://docs.docker.com/engine/network/port-publishing/
- 7: https://docs.docker.com/engine/network/packet-filtering-firewalls
- 8: https://docs.docker.com/engine/network/firewall-iptables/
Replace the universal shared-infra trust boundary with scoped external networks.
shared-infra currently connects product containers directly to observability exporters and privileged host-facing services. On Docker user-defined networks, containers resolve each other by name and can reach each other inside the network even if no host port is published.
If shared access is required, create separate networks for each allowed flow, such as product-to-Alloy telemetry, Grafana-to-Keycloak identity checks, and postgres-exporter-to-postgres monitoring. Keep node-exporter and privileged cAdvisor off product-facing networks unless a documented consumer permits container-level access. Update ARCHITECTURE.md, the release note, and the verifier allowlists to enforce per-service memberships instead of requiring every service to join shared-infra.
📍 Affects 6 files
ARCHITECTURE.md#L680-L685(this comment)infrastructure/deployment/compose.docs.yaml#L11-L13infrastructure/deployment/compose.production.yaml#L421-L424infrastructure/observability/compose.observability.yaml#L185-L187infrastructure/observability/compose.observability.yaml#L205-L207infrastructure/deployment/scripts/verify-shared-dns-network.py#L192-L196.tegami/2026-08-06-shared-service-dns.md#L9-L12
🤖 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 `@ARCHITECTURE.md` around lines 680 - 685, Replace universal shared-infra
membership with scoped external networks for each permitted flow, keeping
product and observability-private networks intact and excluding node-exporter
and privileged cAdvisor unless explicitly required. Apply the network and
service-membership updates in ARCHITECTURE.md,
infrastructure/deployment/compose.docs.yaml (lines 11-13),
infrastructure/deployment/compose.production.yaml (lines 421-424),
infrastructure/observability/compose.observability.yaml (lines 185-187 and
205-207), and update
infrastructure/deployment/scripts/verify-shared-dns-network.py (lines 192-196)
allowlists to enforce those scoped memberships. Update
.tegami/2026-08-06-shared-service-dns.md (lines 9-12) to document the new
per-flow networks.
Source: Coding guidelines
| actual_ports = service.get("ports") or [] | ||
| expected_ports = EXPECTED_PORTS[relative_path].get(service_name, []) | ||
| if actual_ports != expected_ports: | ||
| failures.append( | ||
| f"{relative_path}:{service_name}: expected host ports " | ||
| f"{expected_ports}, got {actual_ports}" | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
docker compose \
--file infrastructure/observability/compose.observability.yaml \
config --no-interpolate --format json |
jq '{
alloy: .services.alloy.ports,
grafana: .services.grafana.ports,
prometheus: .services.prometheus.ports
}'Repository: kl3inIT/OrgMemory
Length of output: 199
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^docs/|^infrastructure/deployment/scripts/verify-shared-dns-network.py$|^infrastructure/observability/compose\.observability\.yaml$)' || true
echo "== target script outline/size =="
wc -l infrastructure/deployment/scripts/verify-shared-dns-network.py
ast-grep outline infrastructure/deployment/scripts/verify-shared-dns-network.py --view expanded || true
echo "== relevant script sections =="
sed -n '1,260p' infrastructure/deployment/scripts/verify-shared-dns-network.py | cat -n
echo "== observability compose relevant services/ports =="
sed -n '1,260p' infrastructure/observability/compose.observability.yaml | cat -nRepository: kl3inIT/OrgMemory
Length of output: 50373
🌐 Web query:
Docker Compose config --format json ports short format published target output
💡 Result:
When you run the command docker compose config --format json, Docker Compose parses, resolves, and renders the effective configuration of your services [1]. As part of this process, it automatically converts the short-syntax port notation (e.g., "8080:80") into the canonical long-syntax format [1][2]. In the generated JSON output, the ports will appear as a list of objects containing the expanded fields [2]. While the specific keys and structure are consistent with the Compose Specification, users should be aware of the following technical behaviors: 1. Canonical Expansion: Short-form strings like "8080:80" are expanded into fields including target, published, protocol, and others [3][2]. 2. Data Type Representation: In the JSON output, the published port is frequently represented as a string (e.g., "8080") rather than a raw integer, even when a single port is defined [4][5][6]. This is done to maintain compatibility with the Compose Specification, which allows the published field to accept string-based port ranges (e.g., "8080-8090") [3][6]. 3. Consistency: This canonical transformation occurs regardless of the original format provided in your YAML files [7]. It is intended to reflect the actual data model applied to the Docker Engine [1][8]. Because docker compose config is the authoritative source for the final configuration applied by Compose, it is recommended for verifying variable substitution and structure before deployment [1][2]. If you encounter downstream tools that fail due to the string-type representation of port numbers, you may need to implement a pre-processing step to cast those values to integers [4][5].
Citations:
- 1: https://docs.docker.com/reference/cli/docker/compose/config/
- 2: https://docs.docker.com/compose/gettingstarted/
- 3: https://docs.docker.com/reference/compose-file/services/
- 4: docker-compose config renders short-form target/published ports as strings in long-form docker/compose#4880
- 5: docker compose config changes the published: <port_number> to published: "<port_number>" docker/compose#9910
- 6: v2.3.x config yaml output contains ports as strings docker/compose#9306
- 7:
docker-compose configalways outputs (sometimes invalid) long form ofportsanddepends_onregardless ofversiondocker/compose#8140 - 8: [BUG]The conversion between long and short syntax is not equivalent. docker/compose#10491
🌐 Web query:
Docker Compose --no-interpolate environment variable port short syntax config json rendered
💡 Result:
The --no-interpolate flag is a command-line option for the docker compose config command that prevents Docker Compose from expanding environment variables (e.g., ${VAR}) within your configuration files [1][2][3]. When this flag is enabled, variables are preserved in their original ${VAR} format in the output, rather than being replaced by values from your shell or.env files [4][5]. Regarding your query on configuration rendering: 1. Flag Behavior: The --no-interpolate flag specifically instructs Compose to skip the interpolation phase during the configuration processing [4][3]. This is useful when you want to use docker compose config as a tool to merge or validate files without modifying variables that you intend to be expanded later (for instance, by a CI/CD system or at runtime) [5]. 2. Ports and Syntax: - Docker Compose supports short-form port syntax (e.g., "${PORT}:80") [6][7]. During standard processing (without --no-interpolate), these are interpolated first and then expanded into canonical long-syntax fields [6]. - If you use --no-interpolate, Compose does not expand these variables [4]. Because the configuration parser expects specific types (like integers for ports) in the final rendered model, keeping a string like "${PORT}" in the output can lead to validation errors if the tool expects a fully resolved integer [8]. - In general, using --no-interpolate results in a "raw" YAML output that reflects the file content as written, rather than the fully normalized and interpolated model [4]. 3. Alternatives for Literal Strings: If you need to include a literal dollar sign in your Compose file (i.e., prevent interpolation for a specific variable while allowing it elsewhere), you should use a double dollar sign ($$) [9][10]. This tells Compose to treat the subsequent characters as a literal string rather than an environment variable, effectively bypassing interpolation for that specific entry even without the --no-interpolate flag [9].
Citations:
- 1: https://docs.docker.com/reference/cli/docker/compose/config/
- 2: https://manpages.debian.org/trixie/docker-compose/docker-compose-config.1.en.html
- 3: restore support for
config --no-interpolatedocker/compose#11556 - 4: [BUG]
--no-interpolateflag ondocker compose confighas unexpected side effects docker/compose#13614 - 5: Allow turning off environment variable interpolation in
docker-compose configdocker/compose#6367 - 6: https://docs.docker.com/compose/gettingstarted/
- 7: https://stackoverflow.com/questions/38609344/docker-compose-interpolation-of-the-assigned-port
- 8: long form syntax of ports directive doesn't support environment variable moby/moby#35532
- 9: https://docs.docker.com/reference/compose-file/interpolation/
- 10: https://github.com/compose-spec/compose-spec/blob/main/12-interpolation.md
Compare expected ports in the rendered Compose structure.
docker compose config --format json canonicalizes short-form ports into mapping objects, but EXPECTED_PORTS stores the unexpanded strings like "127.0.0.1:${GRAFANA_PORT:-3001}:3000". Normalized rendered values and expected values are different shapes, so the verifier reports Alloy, Grafana, and Prometheus as wrong even when the Compose ports are unchanged. Compare canonical port mapping fields, or store expected mappings in the same schema returned by Compose.
🤖 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/scripts/verify-shared-dns-network.py` around lines
198 - 204, Update the port comparison in the verification flow around
EXPECTED_PORTS and actual_ports so both rendered Compose ports and expectations
use the same canonical mapping schema. Normalize EXPECTED_PORTS entries to the
host/container/protocol mapping fields returned by docker compose config, or
canonicalize both sides before comparing, while preserving the existing failure
reporting for genuine mismatches.
| Every service in this stack joins `shared-infra`, and applications reach the | ||
| collector at `observability-alloy:4318` on that network. The product deployment | ||
| and this independently operated stack are the two halves of the wiring. A | ||
| product deploy does not recreate observability containers: after changing this | ||
| Compose topology, explicitly apply this file from the same merged release, then | ||
| confirm the shared-network DNS checks and trace check below. Never `localhost` — | ||
| Alloy binds `127.0.0.1` on the host, and inside a container that address is the | ||
| container itself. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the configured external network name in the trace check.
Line 20 directs operators to the trace check, but Line 110 always uses --network shared-infra. That command fails when ORGMEMORY_SHARED_INFRA_NETWORK overrides the default network name.
Derive the network name from the same observability.env configuration before running the throwaway container.
🤖 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/observability/README.md` around lines 15 - 22, The trace-check
instructions should use the configured external network name instead of
hardcoding shared-infra. Update the command around the trace check to load or
derive the network value from observability.env, specifically honoring
ORGMEMORY_SHARED_INFRA_NETWORK, and pass that value to the throwaway container’s
network option.
Summary
shared-infraDocker DNS networkValidation
python3 infrastructure/deployment/scripts/verify-shared-dns-network.pydocker compose --file infrastructure/deployment/compose.production.yaml --env-file /apps/orgmemory/.env.production config --quietdocker run --rm -v "$PWD:/repo:ro" -w /repo rhysd/actionlint:1.7.12docker run --rm -v "$PWD:/repo:ro" -w /repo ghcr.io/gitleaks/gitleaks:v8.30.1 detect --source . --no-banner --redact --config .gitleaks.tomlcorepack pnpm release:checkunder Node 24python3 scripts/check_docs.pyDeployment
The product stack deploy remains unchanged. After merge and production deployment, apply the independently operated observability Compose project from the merged revision, then verify all live containers are attached to
shared-infraand resolve prefixed peers over Docker DNS.Summary by CodeRabbit
New Features
Bug Fixes
Documentation