Skip to content

Chore/deploy prod configs#236

Merged
sheepdestroyer merged 3 commits into
masterfrom
chore/deploy-prod-configs
Jul 8, 2026
Merged

Chore/deploy prod configs#236
sheepdestroyer merged 3 commits into
masterfrom
chore/deploy-prod-configs

Conversation

@sheepdestroyer

@sheepdestroyer sheepdestroyer commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Document production deployment user configuration and adjust dashboard and visualizer asset paths for correct routing in production.

Enhancements:

  • Update dataset visualizer asset fetch paths to use relative URLs compatible with the deployed router layout.
  • Adjust dashboard API and visualizer links to use relative paths that work correctly behind the production gateway.

Documentation:

  • Add documentation describing the dedicated sudoless production deployment user and its environment configuration.

Summary by CodeRabbit

  • Documentation

    • Expanded setup/deployment instructions with a “Production Deployment User” section, including dedicated non-root service account guidance, SSH administration, and rootless container configuration details.
  • Bug Fixes

    • Updated dashboard stats fetching to use a computed base path rather than an absolute API route.
    • Fixed navigation and data endpoints by switching the visualizer entry/link to relative URLs and deriving request URLs at runtime for stats, datasets, and annotation saving.

@sourcery-ai

sourcery-ai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates production deployment documentation for a dedicated rootless service user and adjusts front-end/dashboard resource paths for correct resolution when served from non-root paths in production.

Sequence diagram for dashboard and visualizer resource loading with relative paths

sequenceDiagram
    actor User
    participant Browser
    participant Gateway

    User->>Browser: Open /dashboard (or subpath)
    Browser->>Gateway: GET dashboard HTML
    Gateway-->>Browser: dashboard HTML with link visualizer

    Browser->>Gateway: GET api/dashboard-stats
    Gateway-->>Browser: JSON dashboard stats

    User->>Browser: Click Dataset Visualizer link
    Browser->>Gateway: GET visualizer
    Gateway-->>Browser: visualizer HTML

    Browser->>Gateway: GET ../data/classified_dataset.json
    Browser->>Gateway: GET ../data/benchmark_results.json
    Browser->>Gateway: GET ../data/annotations.json
    Browser->>Gateway: POST ../dashboard/save-annotations
Loading

File-Level Changes

Change Details Files
Document production deployment service user and rootless Podman setup.
  • Add a 'Production Deployment User' section under setup & deployment describing the dedicated sudoless service account boy
  • Detail home directory, SSH access configuration, systemd user lingering, and rootless Podman socket configuration for production deployments
README.md
Adjust frontend visualizer and dashboard resource URLs to work when hosted under a non-root path.
  • Change dataset, benchmark, and annotations fetch URLs in the visualizer from absolute /data/... paths to relative ../data/... paths
  • Change dashboard annotations save endpoint in the visualizer from /dashboard/save-annotations to ../dashboard/save-annotations
  • Update dashboard HTML to fetch stats from api/dashboard-stats instead of /api/dashboard-stats and link to the visualizer via visualizer instead of /visualizer to avoid absolute path assumptions
router/static/visualizer.html
router/main.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd74a95b-a355-4556-80d3-199d3057b5bd

📥 Commits

Reviewing files that changed from the base of the PR and between 9bb42ce and e3c7385.

📒 Files selected for processing (3)
  • README.md
  • router/main.py
  • router/static/visualizer.html

📝 Walkthrough

Walkthrough

Client-side dashboard and visualizer URLs now derive from the current path instead of using root-absolute endpoints. The README adds a production deployment section describing the dedicated service account, SSH setup, lingering, and rootless Podman configuration.

Changes

Relative URL Migration

Layer / File(s) Summary
Dashboard relative URL updates
router/main.py
Dashboard stats polling and the Dataset Visualizer header link now use a computed relative base path.
Visualizer relative fetch/POST updates
router/static/visualizer.html
Dataset, benchmark, and annotation requests, plus annotation save POSTs, now use a runtime-computed API base path.

Deployment Documentation

Layer / File(s) Summary
Production deployment user documentation
README.md
New section documents the sudoless service account, lingering, SSH setup, and rootless Podman configuration.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and broadly matches the deployment-related README and path configuration changes.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/deploy-prod-configs

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The hard-coded production service account boy and its specific home/SSH configuration in the README may not generalize well; consider describing this as an example pattern or moving host-specific details to an environment-specific config instead of the main docs.
  • The changes from absolute to relative URLs (e.g., /api/dashboard-statsapi/dashboard-stats, /visualizervisualizer, /data/...../data/...) can behave differently depending on the current route; it would be safer to centralize the base path or confirm these paths still resolve as expected from all relevant locations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The hard-coded production service account `boy` and its specific home/SSH configuration in the README may not generalize well; consider describing this as an example pattern or moving host-specific details to an environment-specific config instead of the main docs.
- The changes from absolute to relative URLs (e.g., `/api/dashboard-stats``api/dashboard-stats`, `/visualizer``visualizer`, `/data/...``../data/...`) can behave differently depending on the current route; it would be safer to centralize the base path or confirm these paths still resolve as expected from all relevant locations.

## Individual Comments

### Comment 1
<location path="router/main.py" line_range="3504" />
<code_context>
             async function refreshDashboard() {{
                 try {{
-                    const res = await fetch("/api/dashboard-stats");
+                    const res = await fetch("api/dashboard-stats");
                     if (!res.ok) throw new Error(`HTTP error! status: ${{res.status}}`);
                     const data = await res.json();
</code_context>
<issue_to_address>
**issue (bug_risk):** Using a relative path for `api/dashboard-stats` may break the call when the dashboard is not at the root URL.

Making this URL relative means that on a page like `/dashboard` it will call `/dashboard/api/dashboard-stats` instead of `/api/dashboard-stats`, which will likely 404. Unless the API path has actually moved under the dashboard route, keep the leading `/` so the request always goes to the root `/api/dashboard-stats` endpoint.
</issue_to_address>

### Comment 2
<location path="router/main.py" line_range="3599" />
<code_context>
             <div class="dashboard-title">System Control Center</div>
             <div style="margin-top:8px;font-size:12px;opacity:0.6;">
-                <a href="/visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>
+                <a href="visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>
             </div>
         </header>
</code_context>
<issue_to_address>
**issue (bug_risk):** Relative `visualizer` link may now point to `/dashboard/visualizer` instead of `/visualizer`.

Using `visualizer` makes this a relative URL, so under a `/dashboard` route it will resolve to `/dashboard/visualizer`, which may not exist. If the visualizer is still meant to be at `/visualizer`, keep the leading slash.
</issue_to_address>

### Comment 3
<location path="README.md" line_range="361-366" />
<code_context>
+- **User**: `boy`
</code_context>
<issue_to_address>
**nitpick (typo):** Consider rephrasing "sudoless" to a more standard form like "sudo-less" or "without sudo privileges".

In the bullet "Completely sudoless (no administrative or `wheel` group privileges)", this wording may appear as a typo; a clearer phrase would help avoid confusion.

Suggested implementation:

```
For secure production deployments, the gateway services are configured to run under a dedicated, non-privileged (no sudo) service account:

```

```
- **Security Profile**: Completely without sudo privileges (no administrative or `wheel` group memberships) to minimize container breakout risks.

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread router/main.py Outdated
async function refreshDashboard() {{
try {{
const res = await fetch("/api/dashboard-stats");
const res = await fetch("api/dashboard-stats");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Using a relative path for api/dashboard-stats may break the call when the dashboard is not at the root URL.

Making this URL relative means that on a page like /dashboard it will call /dashboard/api/dashboard-stats instead of /api/dashboard-stats, which will likely 404. Unless the API path has actually moved under the dashboard route, keep the leading / so the request always goes to the root /api/dashboard-stats endpoint.

Comment thread router/main.py Outdated
<div class="dashboard-title">System Control Center</div>
<div style="margin-top:8px;font-size:12px;opacity:0.6;">
<a href="/visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>
<a href="visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Relative visualizer link may now point to /dashboard/visualizer instead of /visualizer.

Using visualizer makes this a relative URL, so under a /dashboard route it will resolve to /dashboard/visualizer, which may not exist. If the visualizer is still meant to be at /visualizer, keep the leading slash.

Comment thread README.md

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds documentation for a dedicated production deployment user in the README and updates several absolute URLs to relative paths in router/main.py and router/static/visualizer.html to better support subpath deployments. The review feedback highlights that these hardcoded relative paths can still fail with 404 errors depending on whether the application is accessed with a trailing slash or deployed under a subpath. To resolve this, the reviewer suggests dynamically determining the base path in JavaScript for both the dashboard and the visualizer assets.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread router/static/visualizer.html Outdated
async function init() {
try {
const resp = await fetch('/data/classified_dataset.json');
const resp = await fetch('../data/classified_dataset.json');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using the relative path ../data/classified_dataset.json works when the visualizer is accessed via /static/visualizer.html or /visualizer at the root of the domain. However, if the application is deployed under a subpath (e.g., /gateway/), accessing /gateway/visualizer will cause the browser to resolve ../data/classified_dataset.json to /data/classified_dataset.json (at the root of the domain, completely bypassing the /gateway/ subpath), resulting in a 404 error.

Conversely, using ./data/classified_dataset.json would work for /gateway/visualizer but break when accessed via /gateway/static/visualizer.html because of the different path depths.

To support both root and subpath deployments robustly across both endpoints, you can dynamically determine the base path in JavaScript:

const apiBase = window.location.pathname.includes('/static/') ? '../' : './';

You can define this locally and prepend it to all fetch requests in this file.

Suggested change
const resp = await fetch('../data/classified_dataset.json');
const apiBase = window.location.pathname.includes('/static/') ? '../' : './';
const resp = await fetch(apiBase + 'data/classified_dataset.json');

Comment thread router/static/visualizer.html Outdated
// Load benchmark results if available
try {
const resp = await fetch('/data/benchmark_results.json');
const resp = await fetch('../data/benchmark_results.json');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using the relative path ../data/benchmark_results.json will bypass any subpath deployment (e.g., /gateway/) when accessed via /gateway/visualizer, resolving to /data/benchmark_results.json at the domain root and causing a 404.

To make this robust for both root and subpath deployments, use a dynamically resolved base path.

Suggested change
const resp = await fetch('../data/benchmark_results.json');
const apiBase = window.location.pathname.includes('/static/') ? '../' : './';
const resp = await fetch(apiBase + 'data/benchmark_results.json');

Comment thread router/static/visualizer.html Outdated
// Load annotations
try {
const resp = await fetch('/data/annotations.json');
const resp = await fetch('../data/annotations.json');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using the relative path ../data/annotations.json will bypass any subpath deployment (e.g., /gateway/) when accessed via /gateway/visualizer, resolving to /data/annotations.json at the domain root and causing a 404.

To make this robust for both root and subpath deployments, use a dynamically resolved base path.

Suggested change
const resp = await fetch('../data/annotations.json');
const apiBase = window.location.pathname.includes('/static/') ? '../' : './';
const resp = await fetch(apiBase + 'data/annotations.json');

Comment thread router/static/visualizer.html Outdated
localStorage.setItem('classifier-annotations', JSON.stringify(annotations));
// Auto-sync by posting to dashboard save endpoint
fetch('/dashboard/save-annotations', {
fetch('../dashboard/save-annotations', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using the relative path ../dashboard/save-annotations will bypass any subpath deployment (e.g., /gateway/) when accessed via /gateway/visualizer, resolving to /dashboard/save-annotations at the domain root and causing a 404.

To make this robust for both root and subpath deployments, use a dynamically resolved base path.

Suggested change
fetch('../dashboard/save-annotations', {
const apiBase = window.location.pathname.includes('/static/') ? '../' : './';
fetch(apiBase + 'dashboard/save-annotations', {

Comment thread router/main.py Outdated
async function refreshDashboard() {{
try {{
const res = await fetch("/api/dashboard-stats");
const res = await fetch("api/dashboard-stats");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Changing the absolute path /api/dashboard-stats to the relative path api/dashboard-stats can cause 404 errors if the dashboard is accessed with a trailing slash (e.g., /dashboard/). In that case, the browser resolves the relative path to /dashboard/api/dashboard-stats instead of /api/dashboard-stats (or /subpath/api/dashboard-stats if hosted under a subpath).

To make this robust for both root and subpath deployments regardless of trailing slashes, you can dynamically determine the base path:

const basePath = window.location.pathname.endsWith('/') ? '../' : './';
const res = await fetch(basePath + "api/dashboard-stats");
Suggested change
const res = await fetch("api/dashboard-stats");
const basePath = window.location.pathname.endsWith('/') ? '../' : './';
const res = await fetch(basePath + "api/dashboard-stats");

Comment thread router/main.py Outdated
<div class="dashboard-title">System Control Center</div>
<div style="margin-top:8px;font-size:12px;opacity:0.6;">
<a href="/visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>
<a href="visualizer" style="color:#818cf8;text-decoration:none;">📊 Dataset Visualizer</a>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using the relative path visualizer for the link can lead to a 404 error if the dashboard is accessed with a trailing slash (e.g., /dashboard/), as the browser will resolve it to /dashboard/visualizer instead of /visualizer (or /subpath/visualizer).

If you want to support subpath deployments robustly, consider using a relative path but ensuring trailing slashes are redirected, or dynamically resolving the link via JavaScript.

@sheepdestroyer
sheepdestroyer force-pushed the chore/deploy-prod-configs branch from 58ce4fd to e3c7385 Compare July 8, 2026 18:11
@sheepdestroyer
sheepdestroyer merged commit 6da78eb into master Jul 8, 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