Skip to content

feat(devops): add observability compose profile - #594

Merged
AbdulmalikAlayande merged 1 commit into
TegoLabs:mainfrom
Ekpemark:feat/343-observability-compose-profile
Jul 31, 2026
Merged

AbdulmalikAlayande merged 1 commit into
TegoLabs:mainfrom
Ekpemark:feat/343-observability-compose-profile

Conversation

@Ekpemark

Copy link
Copy Markdown
Contributor

What does this PR do?

Does this touch secret-key handling or transaction submission?

  • Yes — see notes above
  • No

Checklist

  • Tests pass (npm test)
  • Type check passes (npx tsc --noEmit)
  • Lint passes (npm run lint)
  • Tests cover the new functionality (TDD preferred — see CONTRIBUTING.md)
  • No unnecessary dependencies added
  • Commit messages follow conventional format
  • No console.log in core logic
  • ADR added if this is a significant design decision (see docs/adr)
  • E2E sandbox tested, if this touches RPC or daemon behavior (see docs/e2e-sandbox.md)

@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@Ekpemark Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a local observability stack with Prometheus and Grafana.
    • Added monitoring for Sorokeep’s metrics endpoint, including clear Up/Down status reporting.
    • Automatically provisions a “Sorokeep Overview” Grafana dashboard and Prometheus data source.
    • Added persistent storage and configurable ports and Grafana credentials.
  • Documentation

    • Added instructions for starting the observability stack and accessing its dashboards.

Walkthrough

Adds a Docker Compose observability overlay with Prometheus scraping Sorokeep metrics and Grafana displaying a provisioned endpoint-status dashboard. Documentation explains setup, and Docker tests validate services, scrape targets, provisioning files, and dashboard content.

Changes

Observability stack

Layer / File(s) Summary
Observability service overlay
docker-compose.observability.yml
Adds profiled Sorokeep, Prometheus, and Grafana services with networking, configurable ports, provisioning mounts, dependencies, restart policies, and persistent volumes.
Metrics and dashboard provisioning
devops/prometheus/prometheus.yml, devops/grafana/provisioning/...
Configures Prometheus to scrape sorokeep:9464 and provisions Grafana with a Prometheus datasource and “Sorokeep Overview” status dashboard.
Operational guide and validation
docs/observability.md, tests/docker/docker-compose.test.ts
Documents startup and access details and validates the overlay, scrape target, provisioning files, dashboard title, and panels.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Sorokeep
  participant Prometheus
  participant Grafana
  Sorokeep->>Prometheus: Expose metrics on sorokeep:9464
  Prometheus->>Prometheus: Scrape up{job="sorokeep"}
  Grafana->>Prometheus: Query Prometheus datasource
  Prometheus-->>Grafana: Return endpoint status
Loading

Suggested reviewers: abdulmalikalayande

Poem

A rabbit hops where dashboards gleam,
Prometheus gathers every stream.
Grafana paints the status bright,
“Up” in green, “Down” in night.
With Compose humming, metrics play—
A tidy burrow wired today.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely matches the main change: adding an observability compose profile.
Description check ✅ Passed The description is related to the PR and issue, even though it is mostly a template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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

🤖 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 `@docker-compose.observability.yml`:
- Around line 17-19: Harden the observability overlay by binding the default
Prometheus and Grafana port mappings to 127.0.0.1 instead of all host
interfaces. Remove --web.enable-lifecycle from the Prometheus command unless
authenticated proxy protection is present, and require non-default Grafana admin
credentials before enabling the overlay.

In `@tests/docker/docker-compose.test.ts`:
- Around line 158-180: Extend the observability compose tests around the
existing OBSERVABILITY_COMPOSE_FILE and observabilityComposeConfig checks with a
smoke test that runs the actual Docker Compose command using the base and
observability files, including the observability profile, and executes config.
Assert the command succeeds so merged networks, overrides, mounts, and profiles
are validated rather than only parsing individual files.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ac8db3a4-b618-47cf-90ed-9df45d11aca1

📥 Commits

Reviewing files that changed from the base of the PR and between 35d9237 and 62d4508.

📒 Files selected for processing (7)
  • devops/grafana/provisioning/dashboards/dashboard.yml
  • devops/grafana/provisioning/dashboards/sorokeep-overview.json
  • devops/grafana/provisioning/datasources/prometheus.yml
  • devops/prometheus/prometheus.yml
  • docker-compose.observability.yml
  • docs/observability.md
  • tests/docker/docker-compose.test.ts
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.45.0)
tests/docker/docker-compose.test.ts

[warning] 20-20: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(COMPOSE_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 24-24: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(OBSERVABILITY_COMPOSE_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 166-166: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(PROMETHEUS_CONFIG_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 176-176: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(GRAFANA_DASHBOARD_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🔇 Additional comments (7)
docker-compose.observability.yml (1)

1-13: LGTM!

Also applies to: 20-31, 38-47

devops/prometheus/prometheus.yml (1)

1-9: LGTM!

devops/grafana/provisioning/datasources/prometheus.yml (1)

1-10: LGTM!

devops/grafana/provisioning/dashboards/dashboard.yml (1)

1-11: LGTM!

devops/grafana/provisioning/dashboards/sorokeep-overview.json (1)

1-38: LGTM!

docs/observability.md (1)

1-9: LGTM!

tests/docker/docker-compose.test.ts (1)

8-26: LGTM!

Comment on lines +17 to +19
- "--web.enable-lifecycle"
ports:
- "${PROMETHEUS_PORT:-9090}:9090"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files:\n'
git ls-files | rg '(^|/)docker-compose\.observability\.yml$|docker-compose.*observability|prometheus|grafana'

printf '\nObservability compose snippet:\n'
if [ -f docker-compose.observability.yml ]; then
  nl -ba docker-compose.observability.yml | sed -n '1,120p'
fi

printf '\nGrep lifecycle/port patterns in repo:\n'
rg -n --hidden --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' 'web\.enable-lifecycle|PROMETHEUS_PORT|GRAFANA_PORT|prometheus.*restart|--web\.enable-lifecycle|admin/admin|GF_SECURITY_ADMIN_PASSWORD|GRAFANA_ADMIN|PROMETHEUS_ADMIN' . || true

printf '\nDocker publish port semantics reference from local install docs (if available):\n'
sed -n '17,22p' docker-compose.observability.yml 2>/dev/null || true

Repository: AbdulmalikAlayande/sorokeep

Length of output: 480


🌐 Web query:

Docker Compose ports short syntax published on all interfaces host IP 127.0.0.1 documentation

💡 Result:

In Docker Compose, the short syntax for ports follows the format [HOST:]CONTAINER[/PROTOCOL] [1][2][3]. If you do not specify a host IP in the short syntax, Docker Compose defaults to binding the port to all available network interfaces (0.0.0.0), which may expose the container to the public internet [1][4][3]. To restrict access so that the container is only reachable from the host machine, you must explicitly include 127.0.0.1 in the port mapping [1][4][3]. For example: ports: - "127.0.0.1:8080:80" This configuration maps the container's port 80 to the host's port 8080, but only on the 127.0.0.1 loopback address [1][2][3]. Key considerations include: - Security: Publishing to 0.0.0.0 by default can bypass host firewall rules if not properly managed [1][3]. - YAML Syntax: When using the short syntax with an IP address, it is recommended to wrap the entire string in quotes to prevent the YAML parser from misinterpreting the values (e.g., as a base-60 float) [2][5][6]. - Alternatives: If you need to avoid specifying the IP for every service, you can configure the Docker daemon to use 127.0.0.1 as the default binding address for published ports, though this is a global setting that affects all containers [4].

Citations:


🌐 Web query:

Prometheus --web.enable-lifecycle endpoint security unauthenticated POST /-/reload /-/quit documentation

💡 Result:

When the --web.enable-lifecycle flag is enabled in Prometheus, the /-/reload and /-/quit endpoints become accessible via HTTP requests [1][2]. These endpoints are disabled by default [2][3]. Crucially, Prometheus does not natively enforce authentication on these lifecycle endpoints [1]. If this flag is enabled, any party with network access to the Prometheus HTTP server can trigger a configuration reload or a graceful shutdown of the process [1][4]. To mitigate the security risks associated with enabling these endpoints, the Prometheus documentation recommends the following: 1. Network Isolation: Ensure that the Prometheus server is not exposed to untrusted networks [1]. 2. Reverse Proxy Authentication: If you need to enable these endpoints but require authentication, use an external reverse proxy (e.g., Nginx, Apache, or a dedicated sidecar proxy) to intercept requests and enforce authentication before forwarding them to the Prometheus instance [1]. Alternatively, you can perform these actions without exposing the HTTP endpoints by using system signals: - Reload configuration: Send a SIGHUP signal to the Prometheus process [2][5]. - Shutdown: Send a SIGTERM signal to the Prometheus process [2][5]. Using these signals is the recommended, more secure alternative to using the HTTP lifecycle endpoints when local access to the server is available [2][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path("docker-compose.observability.yml")
text = p.read_text()
print("docker-compose.observability.yml lines:")
for i, line in enumerate(text.splitlines(), 1):
    print(f"{i:3}: {line}")

print("\nRelevant patterns:")
for word in ["--web.enable-lifecycle", "PROMETHEUS_PORT", "GRAFANA_PORT", "admin/admin"]:
    print(f"{word}: {word in text}")
PY

printf '\nGrafana-related security/env defaults:\n'
rg -n --hidden --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' 'admin/admin|GF_SECURITY_ADMIN|ADMIN_PASSWORD|PROMETHEUS_PORT|GRAFANA_PORT|--web\.enable-lifecycle|web\.enable-lifecycle' . || true

Repository: AbdulmalikAlayande/sorokeep

Length of output: 2680


Keep the observability stack local by default.

Docker Compose short port mappings publish on all host interfaces, so the Prometheus lifecycle endpoint and Grafana login are reachable externally unless protected. Bind both defaults to 127.0.0.1 and remove --web.enable-lifecycle unless lifecycle controls are required and wrapped in authentication/proxy controls; also require non-default Grafana admin credentials before enabling this overlay.

Proposed hardening
     command:
       - "--config.file=/etc/prometheus/prometheus.yml"
       - "--storage.tsdb.path=/prometheus"
-      - "--web.enable-lifecycle"
...
-      - "${PROMETHEUS_PORT:-9090}:9090"
+      - "127.0.0.1:${PROMETHEUS_PORT:-9090}:9090"
...
-      - "${GRAFANA_PORT:-3000}:3000"
+      - "127.0.0.1:${GRAFANA_PORT:-3000}:3000"

Also applies to lines 33-37.

📝 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.

Suggested change
- "--web.enable-lifecycle"
ports:
- "${PROMETHEUS_PORT:-9090}:9090"
ports:
- "127.0.0.1:${PROMETHEUS_PORT:-9090}:9090"
🤖 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 `@docker-compose.observability.yml` around lines 17 - 19, Harden the
observability overlay by binding the default Prometheus and Grafana port
mappings to 127.0.0.1 instead of all host interfaces. Remove
--web.enable-lifecycle from the Prometheus command unless authenticated proxy
protection is present, and require non-default Grafana admin credentials before
enabling the overlay.

Comment on lines +158 to +180
describe("observability compose overlay", () => {
it("defines profiled Prometheus and Grafana services", () => {
expect(fs.existsSync(OBSERVABILITY_COMPOSE_FILE)).toBe(true);
expect(observabilityComposeConfig.services.prometheus.profiles).toContain("observability");
expect(observabilityComposeConfig.services.grafana.profiles).toContain("observability");
});

it("wires Prometheus to Sorokeep's metrics endpoint", () => {
expect(fs.existsSync(PROMETHEUS_CONFIG_FILE)).toBe(true);
const prometheusConfig = YAML.parse(fs.readFileSync(PROMETHEUS_CONFIG_FILE, "utf8"));
const targets = prometheusConfig.scrape_configs.flatMap((job: any) =>
job.static_configs.flatMap((config: any) => config.targets),
);
expect(targets).toContain("sorokeep:9464");
});

it("provisions the Sorokeep Grafana dashboard", () => {
expect(fs.existsSync(GRAFANA_DASHBOARD_PROVIDER_FILE)).toBe(true);
expect(fs.existsSync(GRAFANA_DASHBOARD_FILE)).toBe(true);
const dashboard = JSON.parse(fs.readFileSync(GRAFANA_DASHBOARD_FILE, "utf8"));
expect(dashboard.title).toBe("Sorokeep Overview");
expect(dashboard.panels.length).toBeGreaterThan(0);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Validate the merged Compose configuration, not only individual files.

These tests never run docker compose with both Compose files, so they cannot catch merge-time failures in networks, service overrides, mounts, or profiles. Add a docker compose ... config smoke test (or CI validation) for the actual observability command.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 166-166: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(PROMETHEUS_CONFIG_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 176-176: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(GRAFANA_DASHBOARD_FILE, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

🤖 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 `@tests/docker/docker-compose.test.ts` around lines 158 - 180, Extend the
observability compose tests around the existing OBSERVABILITY_COMPOSE_FILE and
observabilityComposeConfig checks with a smoke test that runs the actual Docker
Compose command using the base and observability files, including the
observability profile, and executes config. Assert the command succeeds so
merged networks, overrides, mounts, and profiles are validated rather than only
parsing individual files.

AbdulmalikAlayande added a commit that referenced this pull request Jul 31, 2026
…rimmed to scope)

Adds src/alerts/googlechat.ts and registers it in builtins.ts, per #313.
Registration and the sender itself were already correct.

Trimmed from the original PR before merging: a bundled Grafana/Prometheus
observability stack (devops/grafana/*, devops/prometheus/*, docker-compose.
observability.yml, docs/observability.md) - unrelated to this issue, shared
branch lineage with several other open PRs (#594, #595, #596) that also
carry the identical bundle. Left tests/docker/docker-compose.test.ts
untouched by reverting to main's version.

Updated tests/alerts/builtins.test.ts for the 10th channel (the PR's
branch predated matrix/teams/email, so its own copy of this file didn't
know about them).

Note for a follow-up: src/alerts/discord.ts has the same hostname-
validation weakness fixed in #557 (`hostname.includes("discord")`,
even looser than Teams's check) - pre-existing, not part of this PR,
flagging separately.
@AbdulmalikAlayande
AbdulmalikAlayande merged commit 62d4508 into TegoLabs:main Jul 31, 2026
3 of 5 checks passed
AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
…rimmed to scope)

Adds src/alerts/googlechat.ts and registers it in builtins.ts, per #313.
Registration and the sender itself were already correct.

Trimmed from the original PR before merging: a bundled Grafana/Prometheus
observability stack (devops/grafana/*, devops/prometheus/*, docker-compose.
observability.yml, docs/observability.md) - unrelated to this issue, shared
branch lineage with several other open PRs (#594, #595, #596) that also
carry the identical bundle. Left tests/docker/docker-compose.test.ts
untouched by reverting to main's version.

Updated tests/alerts/builtins.test.ts for the 10th channel (the PR's
branch predated matrix/teams/email, so its own copy of this file didn't
know about them).

Note for a follow-up: src/alerts/discord.ts has the same hostname-
validation weakness fixed in #557 (`hostname.includes("discord")`,
even looser than Teams's check) - pre-existing, not part of this PR,
flagging separately.
AbdulmalikAlayande added a commit that referenced this pull request Sep 6, 2026
Recovers real work from PR #594 (Ekpemark), which GitHub shows as
merged on 2026-07-31 but whose commit is absent from main's current
history — main appears to have diverged/been reset since then,
dropping this and two other merged PRs from the same contributor.
The other two (#595/#337, #596/#316) turned out to be empty
placeholder commits with no real content, so only this one is worth
recovering.

Adds docker-compose.observability.yml (a --profile observability
overlay wiring Prometheus + Grafana against the existing
docker-compose.yaml), Prometheus scrape config, and Grafana dashboard
provisioning — so the stack comes up pre-wired with zero manual setup,
per issue #343. Resolved conflicts with docs/observability.md (a much
larger doc was added separately since this PR was originally merged;
kept it and added a short section for this profile) and
tests/docker/docker-compose.test.ts (kept the typed interfaces added
since, extended them with the fields this PR's tests need).
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.

2 participants