Skip to content

Fix/bugs#13

Merged
Lftobs merged 6 commits into
devfrom
fix/bugs
Jun 19, 2026
Merged

Fix/bugs#13
Lftobs merged 6 commits into
devfrom
fix/bugs

Conversation

@Lftobs

@Lftobs Lftobs commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added Grafana provisioning plus “Deployed Apps” dashboards (CPU, memory, and container log panels) and automatic dashboard creation/updates for running projects.
    • Added scripts to ensure Grafana dashboards are kept in sync.
  • Bug Fixes
    • Improved deployment reliability by reconciling container networking when bringing containers to running state, and preventing dashboard creation failures from breaking deployments.
  • Documentation
    • Updated contribution guidelines; added MIT license.
  • Chores
    • Added security scan workflows and repo issue/PR templates; updated install and Docker Compose monitoring/healthcheck behavior and persistent storage.

Lftobs added 2 commits June 16, 2026 12:09
configuration management

- Update docker-compose and install scripts to
  handle monitoring configs
  and Grafana dashboards more effectively
- Strengthen pipeline workspace cleanup to prevent
  blocking deployments
- Centralize and update system configurations in
  the API
- Add default admin email and network filtering to
  monitoring stack
security CI

- Add GitHub issue templates for bug reports and
  feature requests
- Add a pull request template for standardized
  contributions
- Add a security workflow for scanning for
  forbidden patterns and vulnerabilities
- Add a version bump utility script
- Add Grafana dashboard provisioning for
  monitoring
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Lftobs, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 34 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

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

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, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 50582164-9f6a-46f3-945e-7d85184c5c5e

📥 Commits

Reviewing files that changed from the base of the PR and between 5674aee and 7697084.

📒 Files selected for processing (6)
  • .gitignore
  • CHANGELOG.md
  • VERSION
  • apps/api/package.json
  • apps/docs/package.json
  • apps/web/package.json
📝 Walkthrough

Walkthrough

Adds Grafana dashboard auto-provisioning via both static file provisioning and a new TypeScript utility invoked during deployments. Introduces a security scan CI workflow with forbidden-pattern and Lazarus scanners. Refactors API config defaults via a centralized SYSTEM constant, isolates deployment cleanup error handling, updates docker-compose services with Prometheus TSDB recovery and improved Promtail configuration, and adds community health files (LICENSE, CONTRIBUTING.md, issue/PR templates) plus a version bump script with changelog support.

Changes

Monitoring Infrastructure and Grafana Provisioning

Layer / File(s) Summary
Grafana dashboard provisioning config and deployed-apps dashboard
infra/monitoring/grafana/dashboards/dashboards.yml, infra/monitoring/grafana/dashboards/deployed-apps.json, infra/monitoring/grafana/datasources/prometheus.yml, infra/monitoring/grafana/datasources/loki.yml
Adds the Grafana dashboards provider config enabling file-based discovery, a deployed-apps dashboard JSON with CPU, memory, and log panels parameterized by container variable, and stable uid fields to both datasource YAMLs.
docker-compose service hardening, Prometheus recovery, Caddy defaults, and release workflow
docker-compose.yml, infra/caddy/Caddyfile, .github/workflows/release.yml, infra/monitoring/prometheus-entrypoint.sh
Updates buildkit healthcheck to HTTP wget probe; sets CADDY_EMAIL default to admin@dequel.local; adds Prometheus entrypoint script to recover corrupted TSDB blocks and enable WAL compression with 60s graceful stop; mounts Grafana dashboards and Promtail persistent volumes; restricts Promtail Docker scrape to dequel_net; simplifies release tarball to recursively copy full infra/monitoring directory.
Install script dashboard directory and non-interactive prompt support
scripts/install.sh
Adds Grafana dashboards directory creation, fetches dashboards.yml and deployed-apps.json during install, rewrites prompt_config to detect TTY availability and generate encryption keys and config files with appropriate permissions.

Grafana API Auto-Provisioning via Deployment Pipeline

Layer / File(s) Summary
config.ts SYSTEM constant and default port/queue value updates
apps/api/src/utils/config.ts
Introduces a SYSTEM constant bundling six system defaults, spreads it into config, changes port to 17474, appInternalPort to 17476, and queueConcurrency to 3, and removes unused FileConfig import.
grafana.ts utility: session login, cache, and ensureProjectDashboard
apps/api/src/utils/grafana.ts
Implements grafanaLogin with 1-hour cookie cache, grafanaPost authenticated JSON helper, and ensureProjectDashboard which computes a slug from projectName, builds a multi-panel dashboard payload with Prometheus and Loki queries parameterized by containerRegex, and submits it to Grafana.
pipeline.ts Grafana provisioning call, runtime network reconciliation, and cleanup isolation
apps/api/src/orchestrator/pipeline.ts, apps/api/src/orchestrator/runtime.ts, apps/api/src/api/projects/index.ts
Calls ensureProjectDashboard after setting deployment status to running; wraps workspace cleanup and archive removal in independent try/catch blocks to isolate cleanup errors; adds missing listDomains import; updates ensureContainerRunning to disconnect/reconnect container from Docker network.
ensure-dashboards batch provisioning scripts
scripts/ensure-dashboards.sh, scripts/ensure-dashboards.py
Bash wrapper exports Grafana/API config and runs Python script; Python script authenticates to Grafana, fetches API projects, enumerates running containers, constructs dashboard payloads with Prometheus and Loki panels, POSTs them to Grafana, and prints per-project results and summary.

Security Scan CI Workflow

Layer / File(s) Summary
Forbidden pattern scan script
scripts/workflow/forbidden-pattern-scan.sh
Strict Bash script that scans tracked files via git grep for the forbidden literal global['!'], reports matches to stderr, and exits non-zero on any hit.
Security Scans CI workflow
.github/workflows/vuln-scan.yml
Adds workflow with concurrency cancellation and read-only permissions; runs Forbidden Pattern Scan and Lazarus Scanner (with SHA-256 verification) jobs on push and pull_request events.

Community Health Files and Version Bump Tooling

Layer / File(s) Summary
LICENSE and CONTRIBUTING.md
LICENSE, CONTRIBUTING.md
Adds MIT License with 2024 Lftobs copyright and comprehensive CONTRIBUTING.md covering setup, conventions, migration, testing, versioning, PR process, release behavior, and questions link.
GitHub issue and PR templates
.github/ISSUE_TEMPLATE/bug_report.md, .github/ISSUE_TEMPLATE/feature_request.md, .github/PULL_REQUEST_TEMPLATE.md
Adds structured bug report and feature request templates with component checkboxes and environment fields, and a PR template with change type checklist and review items.
bump.sh version bump script and root symlink
bump.sh, scripts/workflow/bump.sh
Adds scripts/workflow/bump.sh with MAJOR.MINOR.PATCH validation, VERSION file update, bun sync-versions, interactive CHANGELOG.md entry, and formatted summary; root bump.sh becomes a symlink.

Sequence Diagram(s)

sequenceDiagram
  participant Deploy as runDeployment (pipeline.ts)
  participant Grafana as ensureProjectDashboard (grafana.ts)
  participant GrafanaAPI as Grafana /api

  Deploy->>Deploy: update deployment status → running
  Deploy->>Grafana: ensureProjectDashboard(projectName, containerRegex)
  Grafana->>GrafanaAPI: POST /api/login
  GrafanaAPI-->>Grafana: set-cookie (cached 1hr)
  Grafana->>GrafanaAPI: POST /api/dashboards/db (dashboard JSON)
  GrafanaAPI-->>Grafana: { status, url }
  Grafana-->>Deploy: console.log(url) or console.warn(error)
  Deploy->>Deploy: finally: try cleanupWorkspace() catch
  Deploy->>Deploy: finally: try rm(archive, {force:true}) catch
Loading
sequenceDiagram
  participant Prom as Prometheus (docker-compose)
  participant PEntry as prometheus-entrypoint.sh
  participant ProcFS as /prometheus (TSDB)

  Prom->>PEntry: execute with 60s stop_grace_period
  PEntry->>ProcFS: scan /prometheus/01*/ blocks
  PEntry->>PEntry: verify index, meta.json, chunks/ present
  PEntry->>ProcFS: move corrupted blocks to /prometheus/quarantine
  PEntry->>Prom: exec prometheus (30d retention, WAL compression)
  Prom-->>Prom: running
Loading
sequenceDiagram
  participant CI as GitHub Actions (vuln-scan.yml)
  participant FPS as forbidden-pattern-scan.sh
  participant LScan as lazarus_scanner.py

  CI->>FPS: bash scripts/workflow/forbidden-pattern-scan.sh $GITHUB_WORKSPACE
  FPS->>FPS: git grep for global['!']
  FPS-->>CI: exit 0 (clean) or exit 1 + stderr
  CI->>LScan: curl pinned URL → verify SHA-256 → python3
  LScan-->>CI: scan results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hop, hop, the dashboards glow,
With Prometheus graphs in a row!
A scanner checks each forbidden byte,
The bump script bumps with colorful light,
TSDB blocks recover from the night! 🌟

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'Fix/bugs' is vague and generic, providing no meaningful information about what specific issues are being addressed or what the changeset accomplishes. Use a more descriptive title that captures the main changes, such as 'Add monitoring dashboards, release workflow, and security scanning infrastructure' or a more specific summary of the primary fix.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bugs

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 and usage tips.

Replace YAML issue forms with standard Markdown
templates to reduce
overhead.

@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: 10

🧹 Nitpick comments (7)
.github/workflows/vuln-scan.yml (1)

17-18: ⚡ Quick win

Harden checkout step with persist-credentials: false.

Setting persist-credentials: false prevents the GitHub token from persisting in the local git config, reducing risk if subsequent steps are compromised. Static analysis also flagged unpinned action references—consider pinning to a SHA for tag-poisoning protection.

🛡️ Proposed hardening
             - name: Checkout
-              uses: actions/checkout@v4
+              uses: actions/checkout@v4  # or pin to SHA: actions/checkout@<sha>
+              with:
+                  persist-credentials: false

Apply the same change to the checkout step in the lazarus-scanner job (line 27-28).

🤖 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/vuln-scan.yml around lines 17 - 18, The checkout action in
the vuln-scan workflow lacks security hardening. Add `persist-credentials:
false` to the `actions/checkout@v4` step to prevent GitHub tokens from
persisting in the local git config and reduce compromise risk. Additionally,
consider pinning the action reference from `@v4` to a specific SHA hash to
protect against tag-poisoning attacks. Apply these same hardening measures to
all checkout steps in the workflow file, including those in other job sections.

Source: Linters/SAST tools

scripts/install.sh (1)

140-140: 💤 Low value

Redundant directory creation.

The data directory is already created by setup_directories() at line 57. This mkdir -p call is harmless but unnecessary.

♻️ Proposed cleanup
-	mkdir -p "$INSTALL_DIR/data"
-
 	local ENC_KEY
🤖 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 `@scripts/install.sh` at line 140, Remove the redundant mkdir -p
"$INSTALL_DIR/data" call at line 140 in scripts/install.sh because the data
directory is already created by the setup_directories() function called earlier
in the script. Simply delete this line as it performs duplicate directory
creation that is unnecessary.
.github/PULL_REQUEST_TEMPLATE.md (1)

27-27: ⚡ Quick win

Clarify "no comments" in the code style checklist.

The phrase "no comments" is ambiguous—it could mean "no commented-out code" (good practice) or "no code comments" (which would discourage helpful documentation). Consider rewording for clarity.

💡 Suggested rewording
-- [ ] My code follows the project's code style (no comments, named exports, functional components, etc.)
+- [ ] My code follows the project's code style (no commented-out code, named exports, functional components, etc.)
🤖 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/PULL_REQUEST_TEMPLATE.md at line 27, The phrase "no comments" in the
code style checklist item is ambiguous and could be misinterpreted as
discouraging helpful documentation. Reword this checklist item to clarify that
it specifically refers to avoiding commented-out code while still allowing
necessary documentation comments. Replace the unclear "no comments" phrase with
more explicit language like "no commented-out code" or "no dead code" to make
the intent unambiguous.
.github/ISSUE_TEMPLATE/feature_request.yml (1)

45-60: ⚡ Quick win

Add missing "Docker / Deployment" component option.

The bug report template includes "Docker / Deployment" as a component option, but this feature request template omits it. Deployment-related feature requests are valid use cases and should be supported for consistency.

📝 Proposed fix to add the missing component
       options:
         - API (Backend)
         - Web Dashboard (Frontend)
         - Docs
         - CLI / Install Script
+        - Docker / Deployment
         - Build System (Railpack / BuildKit)
         - Caddy / Ingress
         - Monitoring (Prometheus / Grafana / Loki)
         - Other
🤖 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/ISSUE_TEMPLATE/feature_request.yml around lines 45 - 60, The
feature_request.yml template is missing the "Docker / Deployment" component
option that exists in the bug report template, creating an inconsistency. Add
"Docker / Deployment" to the options list in the Affected Component dropdown to
ensure feature requests related to deployment can be properly categorized,
maintaining parity with the bug report template options.
.github/ISSUE_TEMPLATE/bug_report.yml (1)

83-98: ⚡ Quick win

Add missing "Deployment:" field to match the example.

The description example (line 92) shows **Deployment**: Docker Compose / Bare metal, but the prefilled value template (lines 93-97) omits this field. Users seeing the example will expect a corresponding entry in the template.

📝 Proposed fix to add the missing field
       value: |
         - OS:
         - Docker version:
         - Bun version (if relevant):
         - Browser (if relevant):
+        - Deployment (if relevant):
     validations:
       required: false
🤖 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/ISSUE_TEMPLATE/bug_report.yml around lines 83 - 98, The Environment
textarea's value template is missing a "Deployment:" field entry that is shown
in the description examples. Add a new line with "- Deployment:" to the
prefilled value template in the Environment section so that users see all the
fields documented in the example, making the template complete and consistent
with the provided examples.
apps/api/src/utils/config.ts (1)

55-59: ⚡ Quick win

Verify queueConcurrency default increase is appropriate.

The default queueConcurrency increased from 1 (sequential) to 3 (parallel). This changes the default behavior to process up to 3 deployments concurrently, which could impact resource usage (CPU, memory, buildkit load).

Confirm this default is appropriate for typical deployment scenarios and won't cause resource contention issues.

🤖 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/api/src/utils/config.ts` around lines 55 - 59, The default value for
queueConcurrency in the withFile configuration has been increased from 1 to 3,
enabling parallel processing of up to 3 deployments concurrently. Review and
confirm this default value is appropriate for typical deployment scenarios and
assess whether it could cause resource contention issues with CPU, memory, or
buildkit load. If testing or deployment experience shows that this default
causes problems, reduce it back to a more conservative value such as 1 or 2.
Document the reasoning behind the chosen default value to help future
maintainers understand the performance tradeoff.
scripts/workflow/bump.sh (1)

73-91: ⚡ Quick win

Consider making the target branch flexible.

Line 90 hardcodes the main branch in the push instruction, but this PR targets the dev branch. The assumed branch may not match the user's workflow.

💡 Suggested improvement to detect current branch
 print_summary() {
   local version="$1"
   local changelog_updated="$2"
+  local branch
+  branch=$(git branch --show-current)
   echo ""
   echo -e "${CYAN}=== Summary ===${NC}"
   echo -e "  VERSION:        ${GREEN}$version${NC}"
   echo -e "  package.json:   ${GREEN}synced${NC}"
   if [ "$changelog_updated" = true ]; then
     echo -e "  CHANGELOG:      ${GREEN}updated${NC}"
   else
     echo -e "  CHANGELOG:      ${YELLOW}skipped${NC}"
   fi
   echo ""
   echo -e "Next steps:"
   echo -e "  ${YELLOW}1. Review CHANGELOG.md${NC}"
   echo -e "  ${YELLOW}2. Commit: git add -A && git commit -m \"chore: bump to v$version\"${NC}"
   echo -e "  ${YELLOW}3. Tag:    git tag v$version${NC}"
-  echo -e "  ${YELLOW}4. Push:   git push origin main --tags${NC}"
+  echo -e "  ${YELLOW}4. Push:   git push origin $branch --tags${NC}"
 }
🤖 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 `@scripts/workflow/bump.sh` around lines 73 - 91, The print_summary function
contains a hardcoded "main" branch name in the git push instruction (line 90),
which does not adapt to different branch workflows. Modify the function to
dynamically detect and use the current branch instead of hardcoding "main".
Either pass the current branch as a parameter to print_summary or detect it
within the function using git rev-parse --abbrev-ref HEAD, then update the git
push command to use this dynamic branch name instead of the hardcoded "main"
value.
🤖 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/vuln-scan.yml:
- Around line 1-11: The Security Scans workflow currently runs with default
write-all permissions, but security scanning only requires read access. Add an
explicit permissions block to the workflow (at the top level, after the
concurrency section) that specifies contents: read to follow least-privilege
security practices and prevent potential credential leakage from future
modifications to the workflow.
- Around line 35-42: The workflow downloads lazarus_scanner.py from the main
branch of an external repository without any integrity verification, creating a
supply-chain attack risk. Modify the download step to pin the URL to a specific
commit hash instead of main, and add SHA256 checksum verification after the
download. Capture the expected checksum for that specific commit version and add
a verification step using sha256sum that validates the downloaded file before
the "Run Lazarus scanner" step executes python3 lazarus_scanner.py. The checksum
verification should fail and exit with an error message if the hash does not
match, preventing execution of unverified code.

In `@apps/api/src/utils/config.ts`:
- Around line 26-36: The SYSTEM constant contains hardcoded values for
databasePath, workspaceRoot, caddyRoutesDir, dockerNetwork, buildkitHost, and
redisUrl that were previously configurable via environment variables or config
files. To restore configurability, replace the hardcoded string values in the
SYSTEM constant with calls to withFile for each of these properties, allowing
deployments to override these settings through configuration files or
environment variables as they could previously.
- Around line 37-41: The port and appInternalPort configuration defaults in
config.ts have been changed (from 3001 to 17474 and 3000 to 17476 respectively),
but this change has not been propagated to the supporting infrastructure,
causing a mismatch that will break the application. Update the corresponding
port values in docker-compose.yml (both environment variables and health check
URLs), test files (hardcoded appInternalPort values and reverse proxy targets),
infra/monitoring/prometheus.yml (API targets), and all documentation files
(README, CONTRIBUTING, AGENTS, docs/) to consistently use the new port values;
alternatively, if these port changes were made prematurely, revert the port
configuration changes in config.ts for both the port and appInternalPort fields
to restore consistency.

In `@CONTRIBUTING.md`:
- Around line 34-41: Update the local development guide section in
CONTRIBUTING.md to correct the port numbers and entry point commands for all
three services. Replace the API section comment and command to use port 17474
(the default) with the command `bun run dev`. Update the Web dashboard section
to specify port 5173 for development (not 3000) with the command `bun run dev`.
Update the Docs section to use port 3000 (not 4321) with the command `bun run
dev`. Remove the incorrect file paths (`bun apps/web/src/main.tsx` and `bun
apps/docs/src/main.tsx`) and replace them with the proper `bun run dev` command,
as the original commands reference non-existent entry point files. Optionally
add a note that these commands can also be run from the workspace root with
appropriate filters if configured.

In `@infra/monitoring/promtail-config.yml`:
- Around line 17-19: The source label
`__meta_docker_container_network_dequel_net` on line 17 is not a documented
Docker service discovery meta label, causing the `action: keep` filter to fail
and stop all container log ingestion. Replace
`__meta_docker_container_network_dequel_net` with the correct documented meta
label `__meta_docker_network_name` to properly filter containers by network.

In `@scripts/install.sh`:
- Around line 145-153: The script creates dequel.json with potentially insecure
defaults when running in non-interactive mode (without TTY), where
prompt_config() skips user prompts but still writes the file with empty
CADDY_BASE_DOMAIN and a potentially insecure ENV_ENCRYPTION_KEY. To fix this,
check whether the script is running in a TTY (interactive mode) before executing
the dequel.json creation block that uses cat with the EOF delimiter. Only create
the dequel.json file with the CADDY_BASE_DOMAIN, ENV_ENCRYPTION_KEY, and
GITHUB_APP_NAME values when the script is running interactively. In
non-interactive mode, skip this file creation entirely and update the warning
message to clarify that both .env variables and manual dequel.json configuration
may be required.
- Around line 142-143: The ENC_KEY variable assignment has an insecure fallback
that silently uses a hardcoded string "dev-env-key-change-me" if both openssl
rand and dd commands fail, which could allow insecure keys to be deployed to
production without user awareness. Replace the final fallback clause (the `||
echo "dev-env-key-change-me"` part) with a fail-fast mechanism that logs an
error message and exits the script, ensuring the installation stops immediately
when secure key generation is impossible rather than continuing with a
known-insecure default.

In `@scripts/workflow/bump.sh`:
- Around line 46-71: The sed command in the add_changelog_entry function uses
the non-portable sed -i syntax without a backup extension, which fails on BSD
sed (macOS). Change the sed -i invocation to use sed -i'' (with empty quotes)
instead, which is compatible with both GNU sed and BSD sed. This modification
should be applied to the sed command that appends the changelog entry to the
CHANGELOG.md file.
- Around line 12-18: The validate_version function uses grep with the -P flag
which is GNU-specific Perl regex syntax and not available in BSD grep (used on
macOS). Replace the grep command to use POSIX ERE syntax instead by changing the
flag from -P to -E and converting the Perl regex pattern to POSIX ERE format.
Specifically, in the grep command within validate_version, replace the \d+
patterns with [0-9]+ and use -E flag instead of -P to ensure the pattern works
across both GNU and BSD grep implementations.

---

Nitpick comments:
In @.github/ISSUE_TEMPLATE/bug_report.yml:
- Around line 83-98: The Environment textarea's value template is missing a
"Deployment:" field entry that is shown in the description examples. Add a new
line with "- Deployment:" to the prefilled value template in the Environment
section so that users see all the fields documented in the example, making the
template complete and consistent with the provided examples.

In @.github/ISSUE_TEMPLATE/feature_request.yml:
- Around line 45-60: The feature_request.yml template is missing the "Docker /
Deployment" component option that exists in the bug report template, creating an
inconsistency. Add "Docker / Deployment" to the options list in the Affected
Component dropdown to ensure feature requests related to deployment can be
properly categorized, maintaining parity with the bug report template options.

In @.github/PULL_REQUEST_TEMPLATE.md:
- Line 27: The phrase "no comments" in the code style checklist item is
ambiguous and could be misinterpreted as discouraging helpful documentation.
Reword this checklist item to clarify that it specifically refers to avoiding
commented-out code while still allowing necessary documentation comments.
Replace the unclear "no comments" phrase with more explicit language like "no
commented-out code" or "no dead code" to make the intent unambiguous.

In @.github/workflows/vuln-scan.yml:
- Around line 17-18: The checkout action in the vuln-scan workflow lacks
security hardening. Add `persist-credentials: false` to the
`actions/checkout@v4` step to prevent GitHub tokens from persisting in the local
git config and reduce compromise risk. Additionally, consider pinning the action
reference from `@v4` to a specific SHA hash to protect against tag-poisoning
attacks. Apply these same hardening measures to all checkout steps in the
workflow file, including those in other job sections.

In `@apps/api/src/utils/config.ts`:
- Around line 55-59: The default value for queueConcurrency in the withFile
configuration has been increased from 1 to 3, enabling parallel processing of up
to 3 deployments concurrently. Review and confirm this default value is
appropriate for typical deployment scenarios and assess whether it could cause
resource contention issues with CPU, memory, or buildkit load. If testing or
deployment experience shows that this default causes problems, reduce it back to
a more conservative value such as 1 or 2. Document the reasoning behind the
chosen default value to help future maintainers understand the performance
tradeoff.

In `@scripts/install.sh`:
- Line 140: Remove the redundant mkdir -p "$INSTALL_DIR/data" call at line 140
in scripts/install.sh because the data directory is already created by the
setup_directories() function called earlier in the script. Simply delete this
line as it performs duplicate directory creation that is unnecessary.

In `@scripts/workflow/bump.sh`:
- Around line 73-91: The print_summary function contains a hardcoded "main"
branch name in the git push instruction (line 90), which does not adapt to
different branch workflows. Modify the function to dynamically detect and use
the current branch instead of hardcoding "main". Either pass the current branch
as a parameter to print_summary or detect it within the function using git
rev-parse --abbrev-ref HEAD, then update the git push command to use this
dynamic branch name instead of the hardcoded "main" value.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 426f10f6-efeb-4e8a-89b1-d03f8397cc92

📥 Commits

Reviewing files that changed from the base of the PR and between cecc54a and ffdceb5.

📒 Files selected for processing (21)
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/workflows/release.yml
  • .github/workflows/vuln-scan.yml
  • CONTRIBUTING.md
  • LICENSE
  • apps/api/src/orchestrator/pipeline.ts
  • apps/api/src/utils/config.ts
  • bump.sh
  • docker-compose.yml
  • infra/caddy/Caddyfile
  • infra/monitoring/grafana/dashboards/dashboards.yml
  • infra/monitoring/grafana/dashboards/deployed-apps.json
  • infra/monitoring/grafana/datasources/loki.yml
  • infra/monitoring/grafana/datasources/prometheus.yml
  • infra/monitoring/promtail-config.yml
  • scripts/install.sh
  • scripts/workflow/bump.sh
  • scripts/workflow/forbidden-pattern-scan.sh

Comment thread .github/workflows/vuln-scan.yml
Comment thread .github/workflows/vuln-scan.yml
Comment thread apps/api/src/utils/config.ts
Comment thread apps/api/src/utils/config.ts
Comment thread CONTRIBUTING.md Outdated
Comment on lines +34 to +41
# API (port 3001)
bun apps/api/src/index.ts

# Web dashboard (port 3000)
bun apps/web/src/main.tsx

# Docs (port 4321)
bun apps/docs/src/main.tsx

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for port configurations in API, web, and docs apps
echo "=== Checking API entry point for port configuration ==="
fd -e 'index.ts|main.ts|config.ts' apps/api/src --max-depth 3 -x grep -l '3001\|17474\|PORT' {} \;

echo -e "\n=== Checking Web entry point ==="
fd -e 'main.tsx' apps/web/src --max-depth 2 -x grep -l '3000\|PORT' {} \;

echo -e "\n=== Checking Docs entry point and port ==="
fd -e 'main.tsx' apps/docs/src --max-depth 2 | head -5
fd -e 'main.tsx|index.ts' apps/docs/src --max-depth 2 -x grep -l '4321\|PORT' {} \;

Repository: Lftobs/dequel

Length of output: 1633


🏁 Script executed:

#!/bin/bash
# List directory structure first
echo "=== Repository structure ==="
ls -la apps/ 2>/dev/null | head -20

echo -e "\n=== API src files ==="
find apps/api/src -type f -name "*.ts" -o -name "*.js" 2>/dev/null | head -10

echo -e "\n=== Web src files ==="
find apps/web/src -type f -name "*.tsx" -o -name "*.ts" 2>/dev/null | head -10

echo -e "\n=== Docs src files ==="
find apps/docs/src -type f -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" 2>/dev/null | head -10

echo -e "\n=== Searching for port 3001 references ==="
rg '3001' apps/ -t ts -t tsx -t js -t json 2>/dev/null | head -20

echo -e "\n=== Searching for port 3000 references ==="
rg '3000' apps/ -t ts -t tsx -t js -t json 2>/dev/null | head -20

echo -e "\n=== Searching for port 4321 references ==="
rg '4321' apps/ -t ts -t tsx -t js -t json 2>/dev/null | head -20

echo -e "\n=== Checking API index.ts content ==="
cat apps/api/src/index.ts 2>/dev/null | head -50

echo -e "\n=== Checking Web main.tsx content ==="
cat apps/web/src/main.tsx 2>/dev/null | head -50

echo -e "\n=== Checking Docs main.tsx content ==="
cat apps/docs/src/main.tsx 2>/dev/null | head -50

Repository: Lftobs/dequel

Length of output: 3954


🏁 Script executed:

#!/bin/bash
# Find config files in docs
echo "=== All files in apps/docs ==="
find apps/docs -type f 2>/dev/null

echo -e "\n=== Check for astro.config or package.json ==="
find apps/docs -name "*.config.*" -o -name "package.json" 2>/dev/null

echo -e "\n=== Full API index.ts ==="
wc -l apps/api/src/index.ts
cat apps/api/src/index.ts

echo -e "\n=== Search for listen/port binding in API ==="
rg 'listen|port|\.listen\|3001' apps/api/ -i 2>/dev/null | head -30

Repository: Lftobs/dequel

Length of output: 6180


🏁 Script executed:

#!/bin/bash
echo "=== API config.ts to find port defaults ==="
cat apps/api/src/utils/config.ts | head -100

echo -e "\n=== Docs astro.config.mjs ==="
cat apps/docs/astro.config.mjs

echo -e "\n=== API package.json scripts ==="
cat apps/api/package.json | grep -A 10 '"scripts"'

echo -e "\n=== Web package.json scripts ==="
cat apps/web/package.json | grep -A 10 '"scripts"'

echo -e "\n=== Docs package.json scripts ==="
cat apps/docs/package.json | grep -A 10 '"scripts"'

echo -e "\n=== CONTRIBUTING.md content around lines 34-41 ==="
cat CONTRIBUTING.md | sed -n '30,45p'

Repository: Lftobs/dequel

Length of output: 3494


Fix incorrect ports and entry point commands in local development guide.

The specified ports and entry point commands are incorrect. API defaults to port 17474 (configured via PORT env var, not 3001), Web dev runs on Vite's default 5173 (preview uses 3000), and Docs is an Astro project running on port 3000—not 4321. More critically, the entry point commands are wrong: bun apps/web/src/main.tsx and bun apps/docs/src/main.tsx files do not exist. Use the package.json scripts instead:

# API (port 17474 by default)
bun run dev

# Web dashboard (port 5173 by default for dev)
bun run dev

# Docs (port 3000 by default)
bun run dev

Or run from the workspace root with workspace filters if configured.

🤖 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 `@CONTRIBUTING.md` around lines 34 - 41, Update the local development guide
section in CONTRIBUTING.md to correct the port numbers and entry point commands
for all three services. Replace the API section comment and command to use port
17474 (the default) with the command `bun run dev`. Update the Web dashboard
section to specify port 5173 for development (not 3000) with the command `bun
run dev`. Update the Docs section to use port 3000 (not 4321) with the command
`bun run dev`. Remove the incorrect file paths (`bun apps/web/src/main.tsx` and
`bun apps/docs/src/main.tsx`) and replace them with the proper `bun run dev`
command, as the original commands reference non-existent entry point files.
Optionally add a note that these commands can also be run from the workspace
root with appropriate filters if configured.

Comment thread infra/monitoring/promtail-config.yml Outdated
Comment thread scripts/install.sh Outdated
Comment thread scripts/install.sh
Comment thread scripts/workflow/bump.sh Outdated
Comment on lines +12 to +18
validate_version() {
local version="$1"
if ! echo "$version" | grep -qP '^\d+\.\d+\.\d+$'; then
echo "Error: Version must be in format MAJOR.MINOR.PATCH (e.g. 0.1.1). Got: $version"
exit 1
fi
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if grep -P is available (GNU grep) or if we need POSIX alternative

# Test grep -P support
if echo "1.2.3" | grep -qP '^\d+\.\d+\.\d+$' 2>/dev/null; then
  echo "grep -P supported (GNU grep)"
else
  echo "grep -P NOT supported (likely BSD grep on macOS)"
fi

# Test portable alternative
if echo "1.2.3" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
  echo "POSIX ERE alternative works"
else
  echo "POSIX ERE alternative failed"
fi

Repository: Lftobs/dequel

Length of output: 114


🏁 Script executed:

# Check for CI/CD workflows that might test on macOS
find . -type f \( -name "*.yml" -o -name "*.yaml" \) | head -20

Repository: Lftobs/dequel

Length of output: 564


🏁 Script executed:

# Check README for system requirements
find . -type f -iname "README*" -exec cat {} \;

Repository: Lftobs/dequel

Length of output: 7392


🏁 Script executed:

# Look for any macOS or platform-specific references
rg -i "macos|darwin|bsd|platform" --max-count=10

Repository: Lftobs/dequel

Length of output: 10562


🏁 Script executed:

# Check release workflow for macOS testing
cat .github/workflows/release.yml

Repository: Lftobs/dequel

Length of output: 2539


🏁 Script executed:

# Check if bump.sh is used in CI/workflows
rg "bump\.sh" --type sh --type yml

Repository: Lftobs/dequel

Length of output: 85


🏁 Script executed:

# Check if scripts are documented for macOS use
rg "macos|osx|apple|darwin" scripts/ README.md --type sh

Repository: Lftobs/dequel

Length of output: 39


🏁 Script executed:

# Check what systems are mentioned in installation docs
find . -name "*.md" -exec grep -l "macOS\|Mac\|Darwin\|BSD" {} \;

Repository: Lftobs/dequel

Length of output: 39


🏁 Script executed:

# Check what bump.sh is and where it's called from
cat scripts/bump.sh

Repository: Lftobs/dequel

Length of output: 105


🏁 Script executed:

# Check install.sh to see if it references bump.sh
cat scripts/install.sh | head -50

Repository: Lftobs/dequel

Length of output: 1449


🏁 Script executed:

# Look for references to bump.sh in documentation or workflows
rg "bump" --type md --type sh --type yml

Repository: Lftobs/dequel

Length of output: 85


🏁 Script executed:

# Search for any file named bump.sh anywhere
find . -name "*bump*" -type f

Repository: Lftobs/dequel

Length of output: 84


🏁 Script executed:

# Check if validate_version function exists anywhere in the codebase
rg "validate_version" --type sh

Repository: Lftobs/dequel

Length of output: 158


🏁 Script executed:

# Read the bump.sh file to see the actual code
cat scripts/workflow/bump.sh

Repository: Lftobs/dequel

Length of output: 3001


Use portable POSIX regex instead of GNU-specific grep -P.

The -P flag uses Perl regex syntax and is only available in GNU grep. While current CI targets Linux, this pattern may not work on macOS (which uses BSD grep). Since the fix is simple and improves portability for any developers or systems using non-GNU grep, use the POSIX ERE alternative:

Suggested fix
 validate_version() {
   local version="$1"
-  if ! echo "$version" | grep -qP '^\d+\.\d+\.\d+$'; then
+  if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
     echo "Error: Version must be in format MAJOR.MINOR.PATCH (e.g. 0.1.1). Got: $version"
     exit 1
   fi
 }
🤖 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 `@scripts/workflow/bump.sh` around lines 12 - 18, The validate_version function
uses grep with the -P flag which is GNU-specific Perl regex syntax and not
available in BSD grep (used on macOS). Replace the grep command to use POSIX ERE
syntax instead by changing the flag from -P to -E and converting the Perl regex
pattern to POSIX ERE format. Specifically, in the grep command within
validate_version, replace the \d+ patterns with [0-9]+ and use -E flag instead
of -P to ensure the pattern works across both GNU and BSD grep implementations.

Comment thread scripts/workflow/bump.sh Outdated
dashboard generation

Add logic to create and update Grafana dashboards
for projects, including
CPU/memory usage and logs via Prometheus and Loki.
Introduce a
supporting Python script for orchestrating
dashboard synchronization.

@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: 5

🧹 Nitpick comments (1)
apps/api/src/orchestrator/pipeline.ts (1)

547-551: ⚡ Quick win

Reuse this.slugify() instead of duplicating slug normalization.

The new Grafana block re-implements the same normalization already defined at Line 238-244. Reusing the method avoids drift.

Suggested change
-				const dashSlug = projectName
-					.toLowerCase()
-					.replace(/[^a-z0-9-]+/g, "-")
-					.replace(/^-+|-+$/g, "")
-					.slice(0, 63);
+				const dashSlug =
+					this.slugify(projectName);
🤖 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/api/src/orchestrator/pipeline.ts` around lines 547 - 551, The dashSlug
variable assignment is re-implementing the same slug normalization logic that
already exists in the this.slugify() method defined earlier in the class.
Replace the multi-line normalization sequence (toLowerCase, replace with regex
patterns, and slice operations) with a single call to this.slugify(projectName)
to eliminate code duplication and prevent future drift between the two
implementations.
🤖 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 `@apps/api/src/utils/config.ts`:
- Around line 116-123: Remove the hardcoded default credentials from the
grafanaUser and grafanaPass configuration entries. Specifically, in the
grafanaUser and grafanaPass fields within the config object, eliminate the
"admin" fallback values that are passed as the second parameter to the withFile
function calls. This ensures that Grafana credentials must be explicitly
provided through environment variables or files rather than silently defaulting
to known credentials, which is a security risk if the application is accessible
beyond local development environments.

In `@apps/api/src/utils/grafana.ts`:
- Around line 50-59: The grafanaPost function returns res.json() without
validating either the HTTP response status (res.ok) or checking the response
body's status field, causing error responses to be treated as successes by the
caller. Fix the grafanaPost function by adding a check for res.ok after the
fetch call, and before returning the parsed JSON, validate that the response
indicates success (checking both the HTTP status and any status field in the
response body if applicable). This ensures the caller can properly distinguish
between successful and failed Grafana API responses.

In `@scripts/ensure-dashboards.py`:
- Around line 24-25: Replace the hardcoded cookie-jar filename
`/tmp/grafana_cookies` with a dynamically generated unique temporary file that
includes the current process ID or a unique identifier. Generate this temporary
file with restrictive permissions (mode 0600 or equivalent) at the start of the
script or function, then ensure it is deleted in a finally block or cleanup
handler to guarantee removal even if the script fails. This change should be
applied to all occurrences of the hardcoded `/tmp/grafana_cookies` path
referenced in the cookie-jar arguments.
- Line 217: Remove the unnecessary f-string prefix from the "title" field value.
The string "Container Logs" does not contain any variable interpolations or
expressions in curly braces, so it should be a plain string literal without the
f prefix. Change f"Container Logs" to "Container Logs" to fix the F541 lint
error.
- Around line 13-43: Add timeout enforcement and error handling to both
subprocess.run calls and JSON parsing operations. For the login subprocess call
and the result subprocess call that queries the API projects endpoint, add a
timeout parameter to prevent indefinite hangs and check the returncode attribute
to verify successful execution. Wrap both json.loads calls that parse
login.stdout and result.stdout with try-except blocks to handle potential
JSONDecodeError exceptions, and ensure you gracefully exit with an appropriate
error message if JSON parsing fails or command execution returns a non-zero exit
code.

---

Nitpick comments:
In `@apps/api/src/orchestrator/pipeline.ts`:
- Around line 547-551: The dashSlug variable assignment is re-implementing the
same slug normalization logic that already exists in the this.slugify() method
defined earlier in the class. Replace the multi-line normalization sequence
(toLowerCase, replace with regex patterns, and slice operations) with a single
call to this.slugify(projectName) to eliminate code duplication and prevent
future drift between the two implementations.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 276f7040-d991-44d8-ba37-6590735b0383

📥 Commits

Reviewing files that changed from the base of the PR and between ffdceb5 and b4dda9e.

📒 Files selected for processing (12)
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/workflows/vuln-scan.yml
  • apps/api/src/api/projects/index.ts
  • apps/api/src/orchestrator/pipeline.ts
  • apps/api/src/utils/config.ts
  • apps/api/src/utils/grafana.ts
  • infra/monitoring/promtail-config.yml
  • scripts/ensure-dashboards.py
  • scripts/ensure-dashboards.sh
  • scripts/install.sh
  • scripts/workflow/bump.sh
✅ Files skipped from review due to trivial changes (4)
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/ISSUE_TEMPLATE/bug_report.md
  • apps/api/src/api/projects/index.ts
  • scripts/ensure-dashboards.sh
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/vuln-scan.yml
  • scripts/workflow/bump.sh
  • scripts/install.sh

Comment on lines +116 to +123
grafanaUser: withFile<string>(
"GRAFANA_USER",
"admin",
),
grafanaPass: withFile<string>(
"GRAFANA_PASS",
"admin",
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid shipping admin/admin as Grafana credential defaults.

Line 116-123 introduces known credentials as runtime fallback. If Grafana is reachable beyond local dev, this materially weakens security. Prefer explicit secrets (env/file) and no privileged default.

Suggested change
  grafanaUser: withFile<string>(
    "GRAFANA_USER",
-   "admin",
+   "",
  ),
  grafanaPass: withFile<string>(
    "GRAFANA_PASS",
-   "admin",
+   "",
  ),
📝 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
grafanaUser: withFile<string>(
"GRAFANA_USER",
"admin",
),
grafanaPass: withFile<string>(
"GRAFANA_PASS",
"admin",
),
grafanaUser: withFile<string>(
"GRAFANA_USER",
"",
),
grafanaPass: withFile<string>(
"GRAFANA_PASS",
"",
),
🤖 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/api/src/utils/config.ts` around lines 116 - 123, Remove the hardcoded
default credentials from the grafanaUser and grafanaPass configuration entries.
Specifically, in the grafanaUser and grafanaPass fields within the config
object, eliminate the "admin" fallback values that are passed as the second
parameter to the withFile function calls. This ensures that Grafana credentials
must be explicitly provided through environment variables or files rather than
silently defaulting to known credentials, which is a security risk if the
application is accessible beyond local development environments.

Comment on lines +50 to +59
const res = await fetch(`${config.grafanaUrl}/api${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: cookie,
},
body: JSON.stringify(body),
});
return await res.json();
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "grafana.ts" -path "*/apps/api/*"

Repository: Lftobs/dequel

Length of output: 89


🏁 Script executed:

cat -n ./apps/api/src/utils/grafana.ts

Repository: Lftobs/dequel

Length of output: 7061


🏁 Script executed:

rg "grafanaPost" ./apps/api/src/

Repository: Lftobs/dequel

Length of output: 213


🌐 Web query:

Grafana API error response format HTTP POST

💡 Result:

Grafana’s HTTP API error responses (typical legacy /api POST endpoints) are JSON objects using Grafana’s “generic error” body model: { "message": "...", // required, human-readable "error": "...", // optional; included only in developer mode "status": "..." // optional; status/cause label } [1] Grafana’s backend implements these HTTP JSON responses via helper functions (for example, it builds JSON with message fields and can include detailed error info depending on runtime/developer-mode behavior) [2]. Important: Grafana also documents a newer /apis route that uses a Kubernetes-style object structure for API responses, which is not the same as the legacy /api error payload shape [3]. If you share the exact endpoint path you’re calling and the HTTP status code, I can narrow down whether that specific endpoint uses additional/alternative error fields beyond {message, error, status}.

Citations:


🌐 Web query:

Grafana /api/dashboards/db POST response format success error

💡 Result:

The /api/dashboards/db endpoint is a legacy API endpoint for creating or updating dashboards [1][2]. Its response format differs from the newer, standardized Grafana API structure [1][3]. Success Response When a dashboard is successfully created or updated, the API returns an HTTP 200 OK status with a JSON body containing the following key fields: - id: The internal database identifier of the dashboard [1][2]. - uid: The unique identifier for the dashboard [1]. - url: The relative URL path for the dashboard [1]. - status: A string indicating the result, typically "success" [1][2]. - version: The updated version number of the dashboard [1][2]. - slug: The URL-friendly version of the dashboard title [1]. Error Responses In the event of an error, the API returns an appropriate HTTP status code (e.g., 412 for precondition failures, 400 for bad requests) [1][2]. The response body is a JSON object typically containing: - message: A descriptive string explaining the error [1]. - status: A string indicating the specific error type (e.g., "version-mismatch" for a 412 error) [1]. Modern API Note Grafana is transitioning to a standardized API structure [4][3]. For newer implementations, the endpoint /apis/dashboard.grafana.app/v1/namespaces/:namespace/dashboards is preferred [5][4]. This modern structure uses a Kubernetes-style resource format with kind, apiVersion, and metadata fields in the response body [4][3].

Citations:


Check HTTP status and Grafana response status before logging success.

The grafanaPost function returns res.json() without checking res.ok, and the caller treats any truthy result as success. This causes false success logs when Grafana returns error responses (HTTP 4xx/5xx), which still parse as JSON objects with message and status fields.

The /api/dashboards/db endpoint returns HTTP status codes (200 for success, 4xx/5xx for errors) alongside JSON bodies containing a status field (e.g., "success" or "version-mismatch"). Current code at line 230 checks if (result), which is true for both success and error responses.

Suggested change
 async function grafanaPost(
 	path: string,
 	body: unknown,
 ): Promise<unknown | null> {
 	const cookie = await grafanaLogin();
 	if (!cookie) return null;
 	try {
 		const res = await fetch(`${config.grafanaUrl}/api${path}`, {
 			method: "POST",
 			headers: {
 				"Content-Type": "application/json",
 				Cookie: cookie,
 			},
 			body: JSON.stringify(body),
 		});
+		if (!res.ok) return null;
 		return await res.json();
 	} catch {
 		return null;
 	}
 }
@@
-	const result = await grafanaPost("/dashboards/db", dashboard);
-	if (result) {
+	const result = (await grafanaPost("/dashboards/db", dashboard)) as
+		| { status?: string; message?: string }
+		| null;
+	if (result?.status === "success") {
 		console.log(
 			`[Grafana] Dashboard created/updated for ${projectName}`,
 		);
 	} else {
 		console.warn(
-			`[Grafana] Failed to create dashboard for ${projectName}`,
+			`[Grafana] Failed to create dashboard for ${projectName}: ${result?.message ?? "unknown error"}`,
 		);
 	}
🤖 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/api/src/utils/grafana.ts` around lines 50 - 59, The grafanaPost function
returns res.json() without validating either the HTTP response status (res.ok)
or checking the response body's status field, causing error responses to be
treated as successes by the caller. Fix the grafanaPost function by adding a
check for res.ok after the fetch call, and before returning the parsed JSON,
validate that the response indicates success (checking both the HTTP status and
any status field in the response body if applicable). This ensures the caller
can properly distinguish between successful and failed Grafana API responses.

Comment on lines +13 to +43
login = subprocess.run(
[
"curl",
"-sk",
"-X",
"POST",
f"{GRAFANA_URL}/login",
"-H",
"Content-Type: application/json",
"-d",
json.dumps({"user": GRAFANA_USER, "password": GRAFANA_PASS}),
"-c",
"/tmp/grafana_cookies",
],
capture_output=True,
text=True,
)
resp = json.loads(login.stdout) if login.stdout else {}
if "message" not in resp or resp.get("message") != "Logged in":
print(f"Grafana login failed: {login.stdout[:200]}")
sys.exit(1)
print("Logged into Grafana")

result = subprocess.run(
["curl", "-sk", f"{API_URL}/projects"], capture_output=True, text=True
)
if not result.stdout:
print("API returned empty response")
sys.exit(1)
projects = json.loads(result.stdout)
print(f"Found {len(projects)} projects")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, locate the file
find . -name "ensure-dashboards.py" -type f

Repository: Lftobs/dequel

Length of output: 88


🏁 Script executed:

#!/bin/bash
# If found, read the file to check lines 13-43
if [ -f "scripts/ensure-dashboards.py" ]; then
  wc -l "scripts/ensure-dashboards.py"
fi

Repository: Lftobs/dequel

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Read the specific section of the file
if [ -f "scripts/ensure-dashboards.py" ]; then
  sed -n '1,50p' "scripts/ensure-dashboards.py"
fi

Repository: Lftobs/dequel

Length of output: 1417


🏁 Script executed:

#!/bin/bash
# Check if there are other subprocess.run calls in the file
rg "subprocess\.run" scripts/ensure-dashboards.py -n

Repository: Lftobs/dequel

Length of output: 248


🏁 Script executed:

#!/bin/bash
# Read the rest of the file to check other subprocess.run calls
sed -n '44,65p' scripts/ensure-dashboards.py

Repository: Lftobs/dequel

Length of output: 546


🏁 Script executed:

#!/bin/bash
# Read the cleanup call
sed -n '255,259p' scripts/ensure-dashboards.py

Repository: Lftobs/dequel

Length of output: 237


Add timeout and error handling to subprocess calls and JSON parsing.

Lines 30 and 42 call json.loads() without error handling, which will crash with JSONDecodeError if curl returns non-JSON. Neither subprocess.run() call enforces a timeout or checks return codes, allowing the script to hang indefinitely or attempt to parse error output.

Suggested change
 login = subprocess.run(
     [
         "curl",
         "-sk",
         "-X",
         "POST",
         f"{GRAFANA_URL}/login",
         "-H",
         "Content-Type: application/json",
         "-d",
         json.dumps({"user": GRAFANA_USER, "password": GRAFANA_PASS}),
         "-c",
         "/tmp/grafana_cookies",
     ],
     capture_output=True,
     text=True,
+    timeout=20,
 )
-resp = json.loads(login.stdout) if login.stdout else {}
+if login.returncode != 0:
+    print(f"Grafana login command failed: {login.stderr[:200]}")
+    sys.exit(1)
+try:
+    resp = json.loads(login.stdout) if login.stdout else {}
+except json.JSONDecodeError:
+    print(f"Grafana login returned non-JSON: {login.stdout[:200]}")
+    sys.exit(1)
 if "message" not in resp or resp.get("message") != "Logged in":
     print(f"Grafana login failed: {login.stdout[:200]}")
     sys.exit(1)
 print("Logged into Grafana")

 result = subprocess.run(
-    ["curl", "-sk", f"{API_URL}/projects"], capture_output=True, text=True
+    ["curl", "-sk", f"{API_URL}/projects"],
+    capture_output=True,
+    text=True,
+    timeout=20,
 )
+if result.returncode != 0:
+    print(f"API request failed: {result.stderr[:200]}")
+    sys.exit(1)
 if not result.stdout:
     print("API returned empty response")
     sys.exit(1)
-projects = json.loads(result.stdout)
+try:
+    projects = json.loads(result.stdout)
+except json.JSONDecodeError:
+    print(f"API returned non-JSON: {result.stdout[:200]}")
+    sys.exit(1)
 print(f"Found {len(projects)} projects")
🧰 Tools
🪛 ast-grep (0.43.0)

[info] 24-24: Do not hardcode temporary file or directory names
Context: "/tmp/grafana_cookies"
Note: [CWE-377].

(hardcoded-tmp-file)


[error] 35-37: Command coming from incoming request
Context: subprocess.run(
["curl", "-sk", f"{API_URL}/projects"], capture_output=True, text=True
)
Note: [CWE-20].

(subprocess-from-request)


[info] 22-22: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"user": GRAFANA_USER, "password": GRAFANA_PASS})
Note: Security best practice.

(use-jsonify)

🪛 Ruff (0.15.17)

[error] 13-13: subprocess call: check for execution of untrusted input

(S603)


[error] 14-26: Starting a process with a partial executable path

(S607)


[error] 25-25: Probable insecure usage of temporary file or directory: "/tmp/grafana_cookies"

(S108)


[error] 36-36: subprocess call: check for execution of untrusted input

(S603)


[error] 37-37: Starting a process with a partial executable path

(S607)

🤖 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 `@scripts/ensure-dashboards.py` around lines 13 - 43, Add timeout enforcement
and error handling to both subprocess.run calls and JSON parsing operations. For
the login subprocess call and the result subprocess call that queries the API
projects endpoint, add a timeout parameter to prevent indefinite hangs and check
the returncode attribute to verify successful execution. Wrap both json.loads
calls that parse login.stdout and result.stdout with try-except blocks to handle
potential JSONDecodeError exceptions, and ensure you gracefully exit with an
appropriate error message if JSON parsing fails or command execution returns a
non-zero exit code.

Comment on lines +24 to +25
"-c",
"/tmp/grafana_cookies",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a unique secure cookie-jar file instead of hardcoded /tmp/grafana_cookies.

A fixed tmp filename is predictable and can be abused on shared systems. Generate a per-run file with restrictive permissions and delete it in finally.

Suggested change
 import json
 import os
 import re
 import subprocess
 import sys
+import tempfile
@@
-login = subprocess.run(
+fd, cookie_path = tempfile.mkstemp(prefix="grafana_cookies_")
+os.close(fd)
+os.chmod(cookie_path, 0o600)
+
+login = subprocess.run(
@@
-        "/tmp/grafana_cookies",
+        cookie_path,
@@
-            "/tmp/grafana_cookies",
+            cookie_path,
@@
-subprocess.run(["rm", "-f", "/tmp/grafana_cookies"], capture_output=True)
+try:
+    os.remove(cookie_path)
+except FileNotFoundError:
+    pass

Also applies to: 63-63, 259-259

🧰 Tools
🪛 ast-grep (0.43.0)

[info] 24-24: Do not hardcode temporary file or directory names
Context: "/tmp/grafana_cookies"
Note: [CWE-377].

(hardcoded-tmp-file)

🪛 Ruff (0.15.17)

[error] 25-25: Probable insecure usage of temporary file or directory: "/tmp/grafana_cookies"

(S108)

🤖 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 `@scripts/ensure-dashboards.py` around lines 24 - 25, Replace the hardcoded
cookie-jar filename `/tmp/grafana_cookies` with a dynamically generated unique
temporary file that includes the current process ID or a unique identifier.
Generate this temporary file with restrictive permissions (mode 0600 or
equivalent) at the start of the script or function, then ensure it is deleted in
a finally block or cleanup handler to guarantee removal even if the script
fails. This change should be applied to all occurrences of the hardcoded
`/tmp/grafana_cookies` path referenced in the cookie-jar arguments.

Source: Linters/SAST tools

{
"id": 3,
"type": "logs",
"title": f"Container Logs",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the unnecessary f-string prefix.

This is a lint error (F541) and should be a plain string literal.

Suggested change
-                    "title": f"Container Logs",
+                    "title": "Container Logs",
📝 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
"title": f"Container Logs",
"title": "Container Logs",
🧰 Tools
🪛 Ruff (0.15.17)

[error] 217-217: f-string without any placeholders

Remove extraneous f prefix

(F541)

🤖 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 `@scripts/ensure-dashboards.py` at line 217, Remove the unnecessary f-string
prefix from the "title" field value. The string "Container Logs" does not
contain any variable interpolations or expressions in curly braces, so it should
be a plain string literal without the f prefix. Change f"Container Logs" to
"Container Logs" to fix the F541 lint error.

Source: Linters/SAST tools

reconciliation

- Add forced network disconnection before starting
  containers to prevent
  network attachment conflicts.

- Update monitoring configuration to handle
  Prometheus block corruption and
  improve Promtail persistence.

- update contributor documentation to
  reflect current deployment and testing
  workflows.

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docker-compose.yml (1)

18-27: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Healthcheck logic incorrectly passes on non-network failures.

The exit status check [ $$? -ne 4 ] will mark the service healthy whenever wget exits with codes other than 4 (network failure). This means the healthcheck passes when buildkit returns HTTP errors (wget exit 8 for 4xx/5xx responses), generic errors (exit 1), SSL failures (exit 5), auth failures (exit 6), and protocol errors (exit 7).

The healthcheck should only pass when wget succeeds (exit 0).

🩺 Proposed fix to check for success only
            test:
                [
                    "CMD-SHELL",
-                    "wget -q -T 2 -O /dev/null http://localhost:1234 >/dev/null 2>&1; [ $$? -ne 4 ]",
+                    "wget -q -T 2 -O /dev/null http://localhost:1234 >/dev/null 2>&1; [ $$? -eq 0 ]",
                ]

Alternatively, rely on Docker Compose's default behavior where non-zero command exit marks the check as failed:

            test:
                [
                    "CMD-SHELL",
-                    "wget -q -T 2 -O /dev/null http://localhost:1234 >/dev/null 2>&1; [ $$? -ne 4 ]",
+                    "wget -q -T 2 -O /dev/null http://localhost:1234",
                ]
🤖 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.yml` around lines 18 - 27, The healthcheck test command in the
docker-compose.yml file currently uses the condition [ $$? -ne 4 ] which marks
the service as healthy for any wget exit code except 4, including HTTP errors,
SSL failures, and other non-network errors. Fix this by changing the exit code
condition to [ $$? -eq 0 ] so the healthcheck only passes when wget successfully
connects and receives a valid response (exit code 0).
🧹 Nitpick comments (1)
docker-compose.yml (1)

167-170: 💤 Low value

Consider conventional entrypoint pattern.

Clearing the entrypoint with entrypoint: [] then setting command: [sh, /entrypoint.sh] is unconventional. The more idiomatic pattern would be:

entrypoint: [sh, /entrypoint.sh]

Or:

entrypoint: sh
command: [/entrypoint.sh]

The current approach is functional but may surprise maintainers familiar with standard Docker Compose patterns.

🤖 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.yml` around lines 167 - 170, The docker-compose configuration
is using an unconventional pattern by setting entrypoint to an empty array and
then using the command field to run the shell script. Replace the current
entrypoint: [] and command: [sh, /entrypoint.sh] pattern with the more idiomatic
approach of using entrypoint: [sh, /entrypoint.sh] directly, which follows
standard Docker Compose conventions and will be more recognizable to maintainers
familiar with typical Docker patterns.
🤖 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 `@apps/api/src/orchestrator/runtime.ts`:
- Around line 79-80: The tryRun function on line 79 that executes docker start
is swallowing failures and allowing the function to return successfully even
when the container failed to start. Replace the tryRun call for the docker start
command with a strict call that does not suppress errors (await the docker start
command directly without tryRun), then add a status check after the start
command to verify the container is actually running before proceeding. You can
keep the network connect operation on line 80 using tryRun as a best-effort
operation, but the docker start command must be strict and validated.

---

Outside diff comments:
In `@docker-compose.yml`:
- Around line 18-27: The healthcheck test command in the docker-compose.yml file
currently uses the condition [ $$? -ne 4 ] which marks the service as healthy
for any wget exit code except 4, including HTTP errors, SSL failures, and other
non-network errors. Fix this by changing the exit code condition to [ $$? -eq 0
] so the healthcheck only passes when wget successfully connects and receives a
valid response (exit code 0).

---

Nitpick comments:
In `@docker-compose.yml`:
- Around line 167-170: The docker-compose configuration is using an
unconventional pattern by setting entrypoint to an empty array and then using
the command field to run the shell script. Replace the current entrypoint: []
and command: [sh, /entrypoint.sh] pattern with the more idiomatic approach of
using entrypoint: [sh, /entrypoint.sh] directly, which follows standard Docker
Compose conventions and will be more recognizable to maintainers familiar with
typical Docker patterns.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5274056-afde-4fc3-8f35-ae60aa086043

📥 Commits

Reviewing files that changed from the base of the PR and between b4dda9e and 5674aee.

📒 Files selected for processing (4)
  • CONTRIBUTING.md
  • apps/api/src/orchestrator/runtime.ts
  • docker-compose.yml
  • infra/monitoring/prometheus-entrypoint.sh
✅ Files skipped from review due to trivial changes (1)
  • CONTRIBUTING.md

Comment on lines 79 to 80
await tryRun(dockerBin, ['start', containerName]);
await tryRun(dockerBin, ['network', 'connect', config.dockerNetwork, containerName]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t swallow docker start failures in the running-state fallback.

Using tryRun on Line 79 can hide a failed container start and let this function return as if recovery succeeded. That creates false readiness and unstable downstream behavior. Keep disconnect/connect best-effort if you want, but start should be strict and followed by a status check.

Suggested fix
   await onLog(`Container ${containerName} did not reach running state — attempting docker start`);
   await tryRun(dockerBin, ['network', 'disconnect', '-f', config.dockerNetwork, containerName]);
-  await tryRun(dockerBin, ['start', containerName]);
+  await run(dockerBin, ['start', containerName]);
   await tryRun(dockerBin, ['network', 'connect', config.dockerNetwork, containerName]);
+  const finalStatus = (await run(dockerBin, ['inspect', '-f', '{{.State.Status}}', containerName])).trim();
+  if (finalStatus !== 'running') {
+    throw new Error(`Container ${containerName} failed to reach running state after docker start`);
+  }
 };
📝 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
await tryRun(dockerBin, ['start', containerName]);
await tryRun(dockerBin, ['network', 'connect', config.dockerNetwork, containerName]);
await run(dockerBin, ['start', containerName]);
await tryRun(dockerBin, ['network', 'connect', config.dockerNetwork, containerName]);
const finalStatus = (await run(dockerBin, ['inspect', '-f', '{{.State.Status}}', containerName])).trim();
if (finalStatus !== 'running') {
throw new Error(`Container ${containerName} failed to reach running state after docker start`);
}
🤖 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/api/src/orchestrator/runtime.ts` around lines 79 - 80, The tryRun
function on line 79 that executes docker start is swallowing failures and
allowing the function to return successfully even when the container failed to
start. Replace the tryRun call for the docker start command with a strict call
that does not suppress errors (await the docker start command directly without
tryRun), then add a status check after the start command to verify the container
is actually running before proceeding. You can keep the network connect
operation on line 80 using tryRun as a best-effort operation, but the docker
start command must be strict and validated.

@Lftobs Lftobs merged commit d94e9ba into dev Jun 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant