Skip to content

Feat/backend versioning#6

Open
DhirenMhatre wants to merge 8 commits into
masterfrom
feat/backend-versioning
Open

Feat/backend versioning#6
DhirenMhatre wants to merge 8 commits into
masterfrom
feat/backend-versioning

Conversation

@DhirenMhatre

Copy link
Copy Markdown

Description

This PR fixes #

Notes for Reviewers

Signed commits

  • Yes, I signed my commits.

mudler added 8 commits April 11, 2026 11:43
Add Version, URI, and Digest fields to BackendMetadata for tracking
installed backend versions and enabling upgrade detection. Add Version
field to GalleryBackend. Add UpgradeAvailable/AvailableVersion fields
to SystemBackend. Implement GetImageDigest() for lightweight OCI digest
lookups via remote.Head. Record version, URI, and digest at install time
in InstallBackend() and propagate version through meta backends.
Add CheckBackendUpgrades() to compare installed backend versions/digests
against gallery entries, and UpgradeBackend() to perform atomic upgrades
with backup-based rollback on failure. Includes Agent A's data model
changes (Version/URI/Digest fields, GetImageDigest).
Add configuration and runtime settings for backend auto-upgrade:
- RuntimeSettings field for dynamic config via API/JSON
- ApplicationConfig field, option func, and roundtrip conversion
- CLI flag with LOCALAI_AUTO_UPGRADE_BACKENDS env var
- Config file watcher support for runtime_settings.json
- Tests for ToRuntimeSettings, ApplyRuntimeSettings, and roundtrip
- Add upgrade check/trigger API endpoints to config and api module
- Backends page: version badge, upgrade indicator, upgrade button
- Manage page: version in metadata, context-aware upgrade/reinstall button
- Settings page: auto-upgrade backends toggle
- UpgradeChecker background service: checks every 6h, auto-upgrades when enabled
- API endpoints: GET /backends/upgrades, POST /backends/upgrades/check, POST /backends/upgrade/:name
- CLI: `localai backends upgrade` command, version display in `backends list`
- BackendManager interface: add UpgradeBackend and CheckUpgrades methods
- Wire upgrade op through GalleryService backend handler
- Distributed mode: fan-out upgrade to worker nodes via NATS
In distributed mode with multiple frontend instances, use PostgreSQL
advisory lock (KeyBackendUpgradeCheck) so only one instance runs
periodic upgrade checks and auto-upgrades. Prevents duplicate
upgrade operations across replicas.

Standalone mode is unchanged (simple ticker loop).
- Test GET /api/backends/upgrades returns 200 (even with no upgrade checker)
- Test POST /api/backends/upgrade/:name accepts request and returns job ID
- Test full upgrade flow: trigger upgrade via API, wait for job completion,
  verify run.sh updated to v2 and metadata.json has version 2.0.0
- Test POST /api/backends/upgrades/check returns 200
- Fix nil check for applicationInstance in upgrade API routes
…kends

- Add upgrade banner on Backends page showing count and Upgrade All button
- Fix upgrade detection for backends installed before version tracking:
  flag as upgradeable when gallery has a version but installed has none
- Fix OCI digest check to flag backends with no stored digest as upgradeable
@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added automatic backend upgrade functionality with periodic checks every 6 hours and PostgreSQL advisory locks for distributed safety.
  • New CLI backends upgrade command and HTTP endpoints for manual and automatic backend upgrades with atomic backup/rollback.
  • React UI now shows version info, upgrade badges, and an "Auto-upgrade Backends" setting.

Key Changes by Area

Backend Management: Added UpgradeBackend() and CheckUpgrades() to BackendManager interface with implementations for local and distributed modes. Uses atomic directory swaps for safe upgrades.

Configuration: New AutoUpgradeBackends flag in ApplicationConfig with CLI flag, env var, and runtime settings support via config file watcher.

HTTP API: New endpoints GET /backends/upgrades, POST /backends/upgrades/check, POST /backends/upgrade/:name with cached upgrade info provider.

OCI: New GetImageDigest() function for cheap version comparisons without full image downloads.

React UI: Upgrade banners, version-aware filtering, per-backend upgrade buttons, and auto-upgrade toggle in Settings.

Files Changed

File Changes Summary
core/application/application.go Integrated UpgradeChecker service into app lifecycle
core/application/config_file_watcher.go Added runtime settings reload for auto-upgrade flag
core/application/startup.go Initialized upgrade checker on startup
core/application/upgrade_checker.go New background service with 6-hour periodic checks and advisory locks
core/cli/backends.go Added backends upgrade command with progress reporting
core/cli/run.go Added --auto-upgrade-backends CLI flag
core/config/application_config.go Added AutoUpgradeBackends boolean field
core/config/application_config_test.go Tests for new config field
core/config/runtime_settings.go Added AutoUpgradeBackends to runtime settings
core/gallery/backend_types.go Added Version, URI, Digest fields to BackendMetadata
core/gallery/backends.go Updated backend listing with version info
core/gallery/backends_version_test.go Tests for version detection logic
core/gallery/upgrade.go New CheckBackendUpgrades() and UpgradeBackend() with atomic swaps
core/gallery/upgrade_test.go Tests for upgrade detection and rollback
core/http/endpoints/localai/backend.go New HTTP handlers for upgrade endpoints
core/http/react-ui/src/pages/Backends.jsx Upgrade banner, filtering toggles, version display
core/http/react-ui/src/pages/Manage.jsx Per-backend upgrade/reinstall buttons
core/http/react-ui/src/pages/Settings.jsx Auto-upgrade backends setting
core/http/react-ui/src/utils/api.js API client methods for upgrade endpoints
core/http/react-ui/src/utils/config.js Config utilities for upgrade settings
core/http/routes/localai.go Route registration for backend upgrade endpoints
core/http/routes/ui_api.go Backend upgrade API implementation
core/http/routes/ui_api_backends_test.go API tests for upgrade detection and job creation
core/services/advisorylock/keys.go Added KeyBackendUpgradeCheck advisory lock key
core/services/galleryop/backends.go Added op.Upgrade flag support
core/services/galleryop/managers.go Extended BackendManager interface with upgrade methods
core/services/galleryop/managers_local.go Local upgrade implementation
core/services/galleryop/operation.go Added Upgrade boolean to ManagementOp
core/services/nodes/managers_distributed.go Distributed upgrade via NATS fan-out
pkg/oci/image.go Added GetImageDigest() for cheap version checks
pkg/oci/image_test.go Tests for digest fetching and invalid refs

Review Focus Areas

  • Advisory lock handling in upgrade_checker.go:47-58 for distributed mode safety.
  • Atomic directory swap rollback logic in upgrade.go:180-210 on upgrade failure.
  • NATS subject reuse in managers_distributed.go:195 (marked TODO for dedicated upgrade subject).

Architecture

Design Decisions:

  • Used PostgreSQL advisory locks (advisorylock.RunLeaderLoop) to ensure single-instance upgrade checks in distributed mode. This trades operational simplicity (no external coordinator) for PostgreSQL dependency.
  • Atomic directory swaps (rename-based) provide rollback safety without complex state machines. Intentionally uses filesystem operations rather than database transactions for backend storage.
  • OCI digest fallback when semantic versioning unavailable. Accepts weaker guarantees for non-semver backends.

Scalability & Extensibility:

  • Distributed mode fans out upgrades via NATS to worker nodes. Current implementation reuses install endpoint with force flag. Dedicated upgrade subject out of scope for this PR.
  • Upgrade info caching via UpgradeInfoProvider interface allows future pluggable backends (S3, etc.) without API changes.

Risks:

  • Intentional: Upgrade checker runs as leader-elected singleton. Failover delay up to 6 hours if leader dies mid-check. Acceptable for non-critical backend updates.
  • Intentional: No transactionality across multiple backend upgrades. Partial upgrade state possible if process killed mid-batch.
  • Unintentional: GetImageDigest() does not validate registry authentication errors distinctly from not-found errors. May mask auth misconfiguration as "no upgrade available".

Merge Status

NOT MERGEABLE — PR Score 14/100, below threshold (50)

  • [H4] PR quality score (14) is below merge floor (50)
  • [H5] 6 HIGH-severity inline review findings need resolution (threshold: 3)
  • [H6] Code quality raw score (24) is below merge floor (40)
  • [H7] 2 HIGH-severity security findings

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Workflow Diagrams

Automatically generated sequence diagrams showing the workflows in this PR

1. Backend Upgrade System with Distributed Coordination

Complex complexity • Components: UpgradeChecker, Application, BackendsUpgrade CLI command

sequenceDiagram
    title Backend Upgrade System Workflow

    participant User as User/Admin
    participant CLI as CLI Commands
    participant App as Application
    participant UC as UpgradeChecker
    participant AL as AdvisoryLock
    participant Gallery as Gallery Service
    participant DB as PostgreSQL
    participant API as HTTP API

    Note over App,UC: Startup Phase
    App->>App: New() startup.go:234
    alt BackendGalleries configured
        App->>UC: NewUpgradeChecker(db)
        Note right of UC: db is nil in standalone modebr/db is authDB in distributed mode
        App->>UC: Run(ctx) as goroutine
        UC->>UC: 30s initial delay
        UC->>Gallery: CheckBackendUpgrades()
        Gallery-->>UC: map of available upgrades
        UC->>UC: Cache results in lastUpgrades
    end

    Note over UC,AL: Distributed Mode Only
    alt db is not nil distributed mode
        UC->>AL: RunLeaderLoop(key, interval)
        loop Every 6 hours
            AL->>DB: Acquire advisory lock
            alt Lock acquired
                AL->>UC: runCheck callback
                UC->>Gallery: CheckBackendUpgrades()
                Gallery-->>UC: upgrade info
                UC->>UC: Update cache
                alt AutoUpgradeBackends enabled
                    UC->>Gallery: UpgradeBackend()
                    Gallery-->>UC: success/failure
                    UC->>Gallery: Re-check for fresh cache
                end
            else Lock not acquired
                Note right of AL: Other instance holds lockbr/Skip periodic check
            end
        end
    else Standalone mode
        UC->>UC: Simple ticker loop
    end

    Note over User,CLI: Manual Operations
    User->>CLI: local-ai backends list
    CLI->>Gallery: CheckBackendUpgrades()
    Gallery-->>CLI: Display upgradeable backends

    User->>CLI: local-ai backends upgrade name
    CLI->>Gallery: UpgradeBackend()
    Gallery-->>CLI: Success/failure result

    Note over User,API: Runtime Configuration
    User->>API: POST runtime settings
    API->>App: ApplyRuntimeSettings
    App->>App: Update AutoUpgradeBackends
    Note right of App: Config watcher appliesbr/settings without restart

    Note over UC,API: On-Demand Operations
    API->>UC: TriggerCheck()
    UC->>UC: Non-blocking trigger to triggerCh
    UC->>Gallery: CheckBackendUpgrades()
    Gallery-->>UC: Fresh results
    UC->>UC: Update cache

    API->>UC: GetAvailableUpgrades()
    UC-->>API: Return cached lastUpgrades copy

    Note over UC: Shutdown
    App->>UC: Shutdown()
    UC->>UC: Close stop channel
    UC->>UC: Wait for done channel
    UC-->>App: Graceful exit
Loading

Note: Diagrams show detected patterns only. Complex workflows may require manual review.

@greptile-apps

greptile-apps Bot commented May 18, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds backend versioning and upgrade management to LocalAI: version/URI/OCI-digest fields are recorded in install metadata, a background UpgradeChecker service periodically compares installed backends against gallery entries, and new CLI commands + REST/UI-API endpoints allow checking and triggering upgrades. The React UI gains an upgrade banner, per-backend upgrade buttons, and a new auto_upgrade_backends runtime setting.

  • core/gallery/upgrade.go implements version-aware and OCI-digest-based upgrade detection (CheckBackendUpgrades) plus an atomic-swap upgrade executor (UpgradeBackend) with backup/rollback.
  • core/application/upgrade_checker.go is a new background goroutine that runs the check loop with a PostgreSQL advisory lock in distributed mode to avoid duplicate work across replicas.
  • HTTP routes, CLI commands, and the React UI are wired to surface upgrade availability and trigger upgrades on demand.

Confidence Score: 3/5

Not safe to merge as-is: the auto-upgrade path is broken in distributed mode and the upgrade executor can silently leave a backend directory missing on a double-rename failure.

The initial runCheck — intentionally run on every instance to warm the cache — also executes UpgradeBackend for every detected upgrade when AutoUpgradeBackends is enabled. This bypasses the advisory lock that was added to prevent concurrent upgrades in distributed deployments, meaning all replicas race to upgrade the same backend directory simultaneously on startup and on every TriggerCheck call. Separately, when the atomic swap in UpgradeBackend fails and the backup restoration rename also fails, the backend directory at its expected path is gone while the backup sits orphaned at backupPath; the returned error does not mention the backup location, leaving operators with no recovery path. Both issues are in the core upgrade execution path that this PR introduces.

core/application/upgrade_checker.go and core/gallery/upgrade.go need the most attention; the distributed advisory-lock gap and the error-handling gap on double-rename failure both live there.

Important Files Changed

Filename Overview
core/application/upgrade_checker.go New background service for periodic backend upgrade detection and auto-upgrade; contains two issues: auto-upgrade bypasses advisory lock on initial/on-demand checks in distributed mode, and Shutdown can panic on double-call.
core/gallery/upgrade.go Core upgrade logic with atomic swap + rollback; error handling when both the swap and rollback rename fail leaves the backend directory missing without surfacing the backup path to the caller.
core/application/application.go Adds UpgradeChecker field and distributedDB helper; distributedDB returns authDB which may be nil in distributed-mode deployments without auth, silently disabling advisory-lock coordination.
core/gallery/backends.go Records version, URI, and OCI digest in install metadata; adds UpgradeAvailable/AvailableVersion fields to SystemBackend — straightforward additions with no obvious issues.
core/services/nodes/managers_distributed.go UpgradeBackend fans out to all healthy nodes by reusing the install endpoint; the TODO comment acknowledges missing a dedicated NATS subject, and the distributed advisory-lock gap applies here too.
core/http/endpoints/localai/backend.go Adds three new endpoints (GET /backends/upgrades, POST /backends/upgrades/check, POST /backends/upgrade/:name) with correct nil checks for the upgrade checker; looks clean.
pkg/oci/image.go Adds GetImageDigest using remote.Head for cheap digest-only fetches; correctly mirrors the auth/transport setup of GetImage.
core/http/routes/ui_api_backends_test.go Integration tests for the upgrade API routes covering job acceptance, actual upgrade execution, and upgrade check endpoints — good coverage of the happy path.

Reviews (1): Last reviewed commit: "feat: upgrade banner with Upgrade All bu..." | Re-trigger Greptile

Comment on lines +150 to +169
uc.lastCheckTime = time.Now()
if err != nil {
xlog.Debug("Backend upgrade check failed", "error", err)
uc.mu.Unlock()
return
}
uc.lastUpgrades = upgrades
uc.mu.Unlock()

if len(upgrades) == 0 {
xlog.Debug("All backends up to date")
return
}

// Log available upgrades
for name, info := range upgrades {
if info.AvailableVersion != "" {
xlog.Info("Backend upgrade available",
"backend", name,
"installed", info.InstalledVersion,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Auto-upgrade bypasses advisory lock on initial check in distributed mode

runCheck is called unconditionally before the advisory-lock loop starts (line 151). Because runCheck also executes UpgradeBackend for each detected upgrade when AutoUpgradeBackends=true, every frontend replica will simultaneously attempt to upgrade the same backends on startup — the advisory lock that is meant to prevent exactly this race does not protect the initial check. Concurrent UpgradeBackend calls on the same backend can corrupt the directory (two instances racing through the backendPath → backupPath → tmpPath → backendPath rename sequence on the same paths).

The same issue applies to TriggerCheck-initiated calls (line 168): on-demand checks run on every instance and also execute auto-upgrades without holding the advisory lock.

Comment thread core/gallery/upgrade.go
Comment on lines +179 to +193
if err := os.Rename(backendPath, backupPath); err != nil {
os.RemoveAll(tmpPath)
return fmt.Errorf("failed to move current backend to backup: %w", err)
}

if err := os.Rename(tmpPath, backendPath); err != nil {
// Restore backup on failure
xlog.Error("Failed to move new backend into place, restoring backup", "error", err)
if restoreErr := os.Rename(backupPath, backendPath); restoreErr != nil {
xlog.Error("Failed to restore backup", "error", restoreErr)
}
os.RemoveAll(tmpPath)
return fmt.Errorf("failed to move new backend into place: %w", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Backend directory lost when backup restoration fails after rename

When os.Rename(tmpPath, backendPath) fails (step 2 of the atomic swap), the code attempts to restore the backup. If that restoration rename also fails, the backend at backendPath is permanently gone — the old content sits stranded at backupPath with no reference in the returned error. The caller only sees "failed to move new backend into place" and has no indication that the backend directory is now missing from its expected path. The subsequent os.RemoveAll(tmpPath) also deletes the new download, leaving both paths orphaned. The error should include the backup path so operators can manually recover.

Comment on lines +193 to +196
uc.mu.Lock()
uc.lastUpgrades = freshUpgrades
uc.mu.Unlock()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Shutdown panics on double-close

Shutdown calls close(uc.stop) with no guard. Calling it a second time, or calling it after the context is already cancelled (which causes Run to return and close uc.done), will panic with "close of closed channel". A sync.Once around the close would make this safe.

Comment on lines +88 to +96

// distributedDB returns the PostgreSQL database for distributed coordination,
// or nil in standalone mode.
func (a *Application) distributedDB() *gorm.DB {
if a.distributed != nil {
return a.authDB
}
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 distributedDB may return nil in distributed mode when auth is not configured

distributedDB returns a.authDB when a.distributed != nil. But authDB is only populated when auth is separately enabled (auth.InitDB); a deployment running distributed mode without auth will have distributed != nil but authDB == nil. In that case NewUpgradeChecker receives a nil DB and silently falls back to standalone mode, meaning every frontend replica runs its own independent periodic-check loop without the advisory-lock coordination that was intended for distributed deployments.


// UpgradeBackend fans out a backend upgrade to all healthy worker nodes.
// TODO: Add dedicated NATS subject for upgrade (currently reuses install with force flag)
func (d *DistributedBackendManager) UpgradeBackend(ctx context.Context, name string, progressCb galleryop.ProgressCallback) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

In distributed mode, POST /api/backends/upgrade/ upgrades only worker nodes because DistributedBackendManager.UpgradeBackend never invokes the local backend manager, so upgrading a backend installed on the frontend itself leaves the frontend on the old version while workers move to the new one.

Suggested change
func (d *DistributedBackendManager) UpgradeBackend(ctx context.Context, name string, progressCb galleryop.ProgressCallback) error {
Call d.local.UpgradeBackend(ctx, name, progressCb) before or after the worker fan-out and include its error in the joined result so the frontend host is upgraded too.
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/services/nodes/managers_distributed.go
Lines: 180-180
Issue Type: functional-high
Severity: high

Issue Description:
In distributed mode, POST /api/backends/upgrade/<name> upgrades only worker nodes because DistributedBackendManager.UpgradeBackend never invokes the local backend manager, so upgrading a backend installed on the frontend itself leaves the frontend on the old version while workers move to the new one.

Current Code:
func (d *DistributedBackendManager) UpgradeBackend(ctx context.Context, name string, progressCb galleryop.ProgressCallback) error {
	allNodes, err := d.registry.List(context.Background())
	if err != nil {
		return err
	}

	galleriesJSON, _ := json.Marshal(d.backendGalleries)
	var errs []error

	for _, node := range allNodes {
		if node.Status != StatusHealthy {
			continue
		}
		// Reuse install endpoint which will re-download the backend (force mode)
		reply, err := d.adapter.InstallBackend(node.ID, name, "", string(galleriesJSON))
		if err != nil {
			if errors.Is(err, nats.ErrNoResponders) {
				xlog.Warn("No NATS responders for node during upgrade, marking unhealthy", "node", node.Name, "nodeID", node.ID)
				d.registry.MarkUnhealthy(context.Background(), node.ID)
				continue
			}
			errs = append(errs, fmt.Errorf("node %s: %w", node.Name, err))
			continue
		}
		if !reply.Success {
			errs = append(errs, fmt.Errorf("node %s: %s", node.Name, reply.Error))
		}
	}

	return errors.Join(errs...)
}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +119 to +121
func (uc *UpgradeChecker) Shutdown() {
close(uc.stop)
<-uc.done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

Shutdown closes uc.stop unconditionally, so a second call will panic; guard it with sync.Once or a non-blocking close path.

Suggested fix
func (uc *UpgradeChecker) Shutdown() {
	uc.shutdownOnce.Do(func() {
		close(uc.stop)
	})
	<-uc.done
}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 119-121
Issue Type: functional-high
Severity: high

Issue Description:
Shutdown closes uc.stop unconditionally, so a second call will panic; guard it with sync.Once or a non-blocking close path.

Current Code:
func (uc *UpgradeChecker) Shutdown() {
	close(uc.stop)
	<-uc.done
}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/cli/backends.go
Comment on lines +75 to +76
// Check for upgrades
upgrades, _ := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness High

The error from CheckBackendUpgrades is discarded here, so return or surface it instead of silently hiding upgrade-check failures.

Suggested fix
	// Check for upgrades
	upgrades, err := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)
	if err != nil {
		return err
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/cli/backends.go
Lines: 75-76
Issue Type: robustness-high
Severity: high

Issue Description:
The error from CheckBackendUpgrades is discarded here, so return or surface it instead of silently hiding upgrade-check failures.

Current Code:
	// Check for upgrades
	upgrades, _ := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/gallery/upgrade.go
Comment on lines +145 to +147
backendPath := filepath.Join(systemState.Backend.BackendsPath, backendName)
tmpPath := backendPath + ".upgrade-tmp"
backupPath := backendPath + ".backup"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security High

backendName is joined directly into a filesystem path without validation, so reject path separators and clean-path escapes before using it in backendPath.

Suggested fix
	if backendName == "" || filepath.Base(backendName) != backendName || backendName == "." || backendName == ".." {
		return fmt.Errorf("invalid backend name %q", backendName)
	}

	backendPath := filepath.Join(systemState.Backend.BackendsPath, backendName)
	tmpPath := backendPath + ".upgrade-tmp"
	backupPath := backendPath + ".backup"
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/gallery/upgrade.go
Lines: 145-147
Issue Type: security-high
Severity: high

Issue Description:
backendName is joined directly into a filesystem path without validation, so reject path separators and clean-path escapes before using it in backendPath.

Current Code:
	backendPath := filepath.Join(systemState.Backend.BackendsPath, backendName)
	tmpPath := backendPath + ".upgrade-tmp"
	backupPath := backendPath + ".backup"

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/gallery/upgrade.go
Comment on lines +158 to +160
uri := downloader.URI(galleryEntry.URI)
if uri.LooksLikeDir() {
if err := cp.Copy(string(uri), tmpPath); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security High

Copying a directory URI without enforcing it stays under a trusted root allows gallery-controlled local path reads, so validate and constrain local directory sources before cp.Copy.

Suggested fix
	uri := downloader.URI(galleryEntry.URI)
	if uri.LooksLikeDir() {
		srcPath := filepath.Clean(string(uri))
		trustedRoot := filepath.Clean(systemState.Backend.BackendsPath)
		rel, err := filepath.Rel(trustedRoot, srcPath)
		if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
			os.RemoveAll(tmpPath)
			return fmt.Errorf("backend source path %q is outside trusted root", srcPath)
		}
		if err := cp.Copy(srcPath, tmpPath); err != nil {
			os.RemoveAll(tmpPath)
			return fmt.Errorf("failed to copy backend from directory: %w", err)
		}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/gallery/upgrade.go
Lines: 158-160
Issue Type: security-high
Severity: high

Issue Description:
Copying a directory URI without enforcing it stays under a trusted root allows gallery-controlled local path reads, so validate and constrain local directory sources before cp.Copy.

Current Code:
	uri := downloader.URI(galleryEntry.URI)
	if uri.LooksLikeDir() {
		if err := cp.Copy(string(uri), tmpPath); err != nil {

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +1228 to +1233
galleryService.BackendGalleryChannel <- galleryop.ManagementOp[gallery.GalleryBackend, any]{
ID: uid.String(),
GalleryElementName: backendName,
Galleries: appConfig.BackendGalleries,
Upgrade: true,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

Sending directly to BackendGalleryChannel can block the handler indefinitely if the channel is unbuffered or saturated, so enqueue in a goroutine or use a bounded non-blocking/select-based send.

Suggested fix
		op := galleryop.ManagementOp[gallery.GalleryBackend, any]{
			ID:                 uid.String(),
			GalleryElementName: backendName,
			Galleries:          appConfig.BackendGalleries,
			Upgrade:            true,
		}
		go func() {
			galleryService.BackendGalleryChannel <- op
		}()
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/routes/ui_api.go
Lines: 1228-1233
Issue Type: functional-high
Severity: high

Issue Description:
Sending directly to BackendGalleryChannel can block the handler indefinitely if the channel is unbuffered or saturated, so enqueue in a goroutine or use a bounded non-blocking/select-based send.

Current Code:
		galleryService.BackendGalleryChannel <- galleryop.ManagementOp[gallery.GalleryBackend, any]{
			ID:                 uid.String(),
			GalleryElementName: backendName,
			Galleries:          appConfig.BackendGalleries,
			Upgrade:            true,
		}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/cli/backends.go
Comment on lines +180 to +205
for name, info := range toUpgrade {
versionStr := ""
if info.AvailableVersion != "" {
versionStr = " to v" + info.AvailableVersion
}
fmt.Printf("Upgrading %s%s...\n", name, versionStr)

progressBar := progressbar.NewOptions(
1000,
progressbar.OptionSetDescription(fmt.Sprintf("downloading %s", name)),
progressbar.OptionShowBytes(false),
progressbar.OptionClearOnFinish(),
)
progressCallback := func(fileName string, current string, total string, percentage float64) {
v := int(percentage * 10)
if err := progressBar.Set(v); err != nil {
xlog.Error("error updating progress bar", "error", err)
}
}

if err := gallery.UpgradeBackend(context.Background(), systemState, modelLoader, galleries, name, progressCallback); err != nil {
fmt.Printf("Failed to upgrade %s: %v\n", name, err)
} else {
fmt.Printf("Backend %s upgraded successfully\n", name)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

The upgrade command returns success even when one or more backend upgrades fail, so accumulate failures and return a non-nil error at the end.

Suggested fix
	var upgradeErrs []error
	for name, info := range toUpgrade {
		versionStr := ""
		if info.AvailableVersion != "" {
			versionStr = " to v" + info.AvailableVersion
		}
		fmt.Printf("Upgrading %s%s...\n", name, versionStr)

		progressBar := progressbar.NewOptions(
			1000,
			progressbar.OptionSetDescription(fmt.Sprintf("downloading %s", name)),
			progressbar.OptionShowBytes(false),
			progressbar.OptionClearOnFinish(),
		)
		progressCallback := func(fileName string, current string, total string, percentage float64) {
			v := int(percentage * 10)
			if err := progressBar.Set(v); err != nil {
				xlog.Error("error updating progress bar", "error", err)
			}
		}

		if err := gallery.UpgradeBackend(context.Background(), systemState, modelLoader, galleries, name, progressCallback); err != nil {
			fmt.Printf("Failed to upgrade %s: %v\n", name, err)
			upgradeErrs = append(upgradeErrs, fmt.Errorf("%s: %w", name, err))
		} else {
			fmt.Printf("Backend %s upgraded successfully\n", name)
		}
	}
	if len(upgradeErrs) > 0 {
		return errors.Join(upgradeErrs...)
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/cli/backends.go
Lines: 180-205
Issue Type: functional-medium
Severity: medium

Issue Description:
The upgrade command returns success even when one or more backend upgrades fail, so accumulate failures and return a non-nil error at the end.

Current Code:
	for name, info := range toUpgrade {
		versionStr := ""
		if info.AvailableVersion != "" {
			versionStr = " to v" + info.AvailableVersion
		}
		fmt.Printf("Upgrading %s%s...\n", name, versionStr)

		progressBar := progressbar.NewOptions(
			1000,
			progressbar.OptionSetDescription(fmt.Sprintf("downloading %s", name)),
			progressbar.OptionShowBytes(false),
			progressbar.OptionClearOnFinish(),
		)
		progressCallback := func(fileName string, current string, total string, percentage float64) {
			v := int(percentage * 10)
			if err := progressBar.Set(v); err != nil {
				xlog.Error("error updating progress bar", "error", err)
			}
		}

		if err := gallery.UpgradeBackend(context.Background(), systemState, modelLoader, galleries, name, progressCallback); err != nil {
			fmt.Printf("Failed to upgrade %s: %v\n", name, err)
		} else {
			fmt.Printf("Backend %s upgraded successfully\n", name)
		}
	}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +46 to +49
if (!preferDevLoaded && data?.preferDevelopmentBackends) {
setShowDevelopment(true)
setPreferDevLoaded(true)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

The server-provided development preference is only marked as loaded when it is true, so a false value causes repeated first-load logic on every refresh; set the loaded flag regardless of the preference value.

Suggested fix
      if (!preferDevLoaded) {
        setShowDevelopment(!!data?.preferDevelopmentBackends)
        setPreferDevLoaded(true)
      }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert javascript developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/react-ui/src/pages/Backends.jsx
Lines: 46-49
Issue Type: functional-medium
Severity: medium

Issue Description:
The server-provided development preference is only marked as loaded when it is true, so a false value causes repeated first-load logic on every refresh; set the loaded flag regardless of the preference value.

Current Code:
      if (!preferDevLoaded && data?.preferDevelopmentBackends) {
        setShowDevelopment(true)
        setPreferDevLoaded(true)
      }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

deleteInstalledBackend: (name) => `/api/backends/system/delete/${name}`,
backendsUpgrades: '/api/backends/upgrades',
backendsUpgradesCheck: '/api/backends/upgrades/check',
upgradeBackend: (name) => `/api/backends/upgrade/${name}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security Medium

The backend name is interpolated into the URL path without encoding, so names containing slashes or reserved characters can target the wrong endpoint; wrap name with encodeURIComponent().

Suggested change
upgradeBackend: (name) => `/api/backends/upgrade/${name}`,
upgradeBackend: (name) => `/api/backends/upgrade/${encodeURIComponent(name)}`,
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert javascript developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/react-ui/src/utils/config.js
Lines: 28-28
Issue Type: security-medium
Severity: medium

Issue Description:
The backend name is interpolated into the URL path without encoding, so names containing slashes or reserved characters can target the wrong endpoint; wrap `name` with `encodeURIComponent()`.

Current Code:
    upgradeBackend: (name) => `/api/backends/upgrade/${name}`,

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Medium
Files Scanned 31

Consider addressing security findings before merging

Scan completed in 26.2s

View vulnerability details (2 items)

1. MATH_RANDOM_USED (CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)) HIGH

File: core/application/application.go (line 5)
Category: Other

Do not use math/rand. Use crypto/rand instead.

Fix: Review and fix the security issue following OWASP guidelines.


2. REACT_DANGEROUSLYSET (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) HIGH

File: core/http/react-ui/src/pages/Backends.jsx (line 576)
Category: Other

Detection of dangerouslySetInnerHTML from non-constant definition. This can inadvertently expose users to cross-site scripting (XSS) attacks if this comes from user-provided input. If you have to use dangerouslySetInnerHTML, consider using a sanitization library such as DOMPurify to sanitize your HTML.

Fix: Review and fix the security issue following OWASP guidelines.


Security scan powered by Codity.ai

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 12
Scanner npm audit
Top 10 Vulnerabilities (12 total found)

1. @hono/node-server various

CVE: GHSA-92pp-h63x-v22m
Severity: MODERATE
Fixed in: True

@hono/node-server: Middleware bypass via repeated slashes in serveStatic


2. brace-expansion various

CVE: GHSA-f886-m6hf-6m8v
Severity: MODERATE
Fixed in: True

brace-expansion: Zero-step sequence causes process hang and memory exhaustion


3. dompurify various

CVE: GHSA-39q2-94rc-95cp, GHSA-h7mw-gpvr-xq4m, GHSA-crv5-9vww-q3g8, GHSA-v9jr-rg53-9pgp
Severity: MODERATE
Fixed in: True

DOMPurify's ADD_TAGS function form bypasses FORBID_TAGS due to short-circuit evaluation


4. express-rate-limit various

CVE: N/A
Severity: MODERATE
Fixed in: True

Vulnerability in express-rate-limit


5. fast-uri various

CVE: GHSA-q3j6-qgpj-74h6, GHSA-v39h-62p7-jpjc
Severity: HIGH
Fixed in: True

fast-uri vulnerable to path traversal via percent-encoded dot segments


6. flatted various

CVE: GHSA-25h7-pfq9-p65f, GHSA-rf6f-7fwh-wjgh
Severity: HIGH
Fixed in: True

flatted vulnerable to unbounded recursion DoS in parse() revive phase


7. hono various

CVE: GHSA-26pp-8wgv-hjvm, GHSA-r5rp-j6wh-rvv4, GHSA-xf4j-xp2r-rqqx, GHSA-wmmm-f939-6g9c, GHSA-458j-xx4x-4375, GHSA-xpcf-pg52-r92g, GHSA-qp7p-654g-cw7p, GHSA-hm8q-7f3q-5f36, GHSA-p77w-8qqv-26rm, GHSA-9vqf-7f2p-gf9v, GHSA-69xw-7hcm-h432
Severity: MODERATE
Fixed in: True

Hono missing validation of cookie name on write path in setCookie()


8. ip-address various

CVE: GHSA-v2v4-37r5-5v8g
Severity: MODERATE
Fixed in: True

ip-address has XSS in Address6 HTML-emitting methods


9. path-to-regexp various

CVE: GHSA-j3q9-mxjg-w52f, GHSA-27v5-c462-wpq7
Severity: HIGH
Fixed in: True

path-to-regexp vulnerable to Denial of Service via sequential optional groups


10. picomatch various

CVE: GHSA-3v7f-55p6-f55p, GHSA-c2c7-rcm5-vvqj
Severity: HIGH
Fixed in: True

Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching


2 more vulnerabilities not shown. Update dependencies to fix these issues.

Powered by Codity.ai · Docs

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 4
Scanner pip-audit
View vulnerability details (4 items)

1. pip 24.0

CVE: GHSA-4xh5-x5gv-qwph
Fixed in: 25.3

When extracting a tar archive pip may not check symbolic links point into the extraction directory if the tarfile module doesn't implement PEP 706. Note that upgrading pip to a "fixed" version for thi


2. pip 24.0

CVE: GHSA-6vgw-5pg2-w6jp
Fixed in: 26.0

When pip is installing and extracting a maliciously crafted wheel archive, files may be extracted outside the installation directory. The path traversal is limited to prefixes of the installation dire


3. pip 24.0

CVE: GHSA-58qw-9mgm-455v
Fixed in:

pip handles concatenated tar and ZIP files as ZIP files regardless of filename or whether a file is both a tar and ZIP file. This behavior could result in confusing installation behavior, such as inst


4. pip 24.0

CVE: GHSA-jp4c-xjxw-mgf9
Fixed in: 26.1

pip prior to version 26.1 would run self-update check functionality after installing wheel files which required importing well-known Python modules names. These module imports were intentionally defer

Powered by Codity.ai · Docs

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 758
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 6
Low Risk (Permissive) 599
Unknown License 153

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 6 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.2

MPL-2.0 (5 packages):

  • github.com/philippgille/chromem-go 0.7.0
  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 153 packages
  • github.com/emirpasic/gods 1.18.1
  • github.com/eritikass/githubmarkdownconvertergo 0.1.10
  • github.com/go-git/go-git/v5 5.16.4
  • github.com/go-git/gcfg 1.5.1-0.20230307220236-3a3c6141e376
  • github.com/go-git/go-billy/v5 5.6.2
  • github.com/gocolly/colly 1.2.0
  • github.com/google/go-github/v69 69.2.0
  • github.com/google/go-querystring 1.1.0
  • github.com/golang/protobuf 1.5.4
  • github.com/kennygrant/sanitize 1.2.4
  • github.com/mudler/skillserver 0.0.6
  • github.com/mschoch/smat 0.2.0
  • github.com/olekukonko/tablewriter 0.0.5
  • github.com/pjbgf/sha1cd 0.3.2
  • github.com/saintfish/chardet 0.0.0-20230101081208-5e3ef4b5456d
  • github.com/sergi/go-diff 1.4.0
  • github.com/segmentio/asm 1.1.3
  • github.com/skeema/knownhosts 1.3.1
  • go.mau.fi/util 0.3.0
  • go.starlark.net 0.0.0-20250417143717-f57e51f710eb

...and 133 more

Powered by Codity.ai · Docs

@codity-ai

codity-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/LocalAI · PR #6

Scanned: 2026-05-18 19:13 UTC | Score: 24/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 1
Medium 3
Low 103
Top Findings

[CQ-LLM-001] core/application/upgrade_checker.go:198 (Complexity · HIGH)

Issue: The 'Run' method in UpgradeChecker has multiple nested select statements, increasing cyclomatic complexity.
Suggestion: Consider refactoring the 'Run' method to reduce nesting and improve readability.

for {
    select {
    case <-ctx.Done():
        return
    case <-uc.stop:
        return
    case <-uc.triggerCh:
        uc.runCheck(ctx)
    }
}

[CQ-LLM-004] core/application/upgrade_checker.go:1 (Documentation · MEDIUM)

Issue: Missing documentation for the 'UpgradeChecker' struct and its methods.
Suggestion: Add docstrings to the 'UpgradeChecker' struct and its methods to improve code documentation.

type UpgradeChecker struct {

[CQ-LLM-002] core/cli/backends.go:134 (Error_Handling · MEDIUM)

Issue: The error from 'json.Unmarshal' is logged but not returned, which may lead to silent failures.
Suggestion: Return the error after logging it to ensure the caller is aware of the failure.

xlog.Error("unable to load galleries", "error", err)

[CQ-LLM-003] core/cli/backends.go:134 (Error_Handling · MEDIUM)

Issue: The error handling for 'CheckBackendUpgrades' does not account for the possibility of nil upgrades.
Suggestion: Add a check for nil before proceeding with operations on 'upgrades'.

upgrades, err := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

[CQ-008] core/application/upgrade_checker.go:73 (Maintainability · LOW)

Issue: Magic number 30 in code
Suggestion: Extract to a named constant

case <-time.After(30 * time.Second):

[CQ-009] core/cli/backends.go:89 (Style · LOW)

Issue: Line exceeds 120 characters (125 chars)
Suggestion: Break long lines into multiple lines for readability

				fmt.Printf(" * %s@%s%s (installed, upgrade available: %s)\n", backend.Gallery.Name, backend.Name, versionStr, upgrad...

[CQ-LLM-005] core/cli/backends.go:134 (Maintainability · LOW)

Issue: The variable 'toUpgrade' is assigned but not used in a clear manner, leading to potential confusion.
Suggestion: Consider renaming 'toUpgrade' to something more descriptive or clarify its usage.

toUpgrade := upgrades

[CQ-008] core/cli/backends.go:194 (Maintainability · LOW)

Issue: Magic number 10 in code
Suggestion: Extract to a named constant

v := int(percentage * 10)

[CQ-009] core/cli/backends.go:200 (Style · LOW)

Issue: Line exceeds 120 characters (131 chars)
Suggestion: Break long lines into multiple lines for readability

		if err := gallery.UpgradeBackend(context.Background(), systemState, modelLoader, galleries, name, progressCallback); e...

[CQ-009] core/cli/run.go:50 (Style · LOW)

Issue: Line exceeds 120 characters (196 chars)
Suggestion: Break long lines into multiple lines for readability

	AutoUpgradeBackends      bool     `env:"LOCALAI_AUTO_UPGRADE_BACKENDS,AUTO_UPGRADE_BACKENDS" help:"Automatically upgrad...

Per-File Breakdown

File Critical High Medium Low Total
core/application/upgrade_checker.go 0 1 1 1 3
core/cli/backends.go 0 0 2 4 6
core/cli/run.go 0 0 0 1 1
core/config/runtime_settings.go 0 0 0 1 1
core/gallery/backends_version_test.go 0 0 0 3 3
core/gallery/upgrade.go 0 0 0 4 4
core/gallery/upgrade_test.go 0 0 0 6 6
core/http/endpoints/localai/backend.go 0 0 0 7 7
core/http/react-ui/src/pages/Backends.jsx 0 0 0 38 38
core/http/react-ui/src/pages/Manage.jsx 0 0 0 20 20
core/http/react-ui/src/pages/Settings.jsx 0 0 0 1 1
core/http/routes/ui_api.go 0 0 0 5 5
core/http/routes/ui_api_backends_test.go 0 0 0 8 8
core/services/advisorylock/keys.go 0 0 0 2 2
core/services/nodes/managers_distributed.go 0 0 0 1 1
pkg/oci/image.go 0 0 0 1 1

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@DhirenMhatre

Copy link
Copy Markdown
Author

@codity review

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

Policy Check Failed

✗ 3/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)
• 28 code file(s) changed but no test files added


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added automatic backend upgrade detection and execution with distributed mode support using PostgreSQL advisory locks
  • New CLI command and HTTP endpoints let users check and trigger upgrades manually or automatically
  • UI now shows version badges, upgrade indicators, and "Upgrade All" functionality

Key Changes by Area

Backend Management: Added UpgradeBackend and CheckUpgrades to BackendManager interface with atomic directory swap upgrades and automatic rollback on failure

API: New endpoints GET /backends/upgrades, POST /backends/upgrades/check, POST /backends/upgrade/:name with distributed coordination via NATS

Configuration: New AutoUpgradeBackends flag and env var for automatic 6-hour upgrade checks

UI: Version display, upgrade banners, filter toggles for meta/development backends, and per-backend upgrade buttons

OCI: New GetImageDigest function fetches image digests without full downloads for cheaper version comparison

Files Changed

File Changes Summary
core/application/application.go Added UpgradeChecker service integration
core/application/config_file_watcher.go Added auto-upgrade config handling
core/application/startup.go Initialize upgrade checker with advisory lock support
core/application/upgrade_checker.go New background service for periodic upgrade checks
core/cli/backends.go Added backends upgrade command with progress bars
core/cli/run.go Added --auto-upgrade-backends flag
core/config/application_config.go Added AutoUpgradeBackends field
core/config/application_config_test.go Tests for new config field
core/config/runtime_settings.go Added AutoUpgradeBackends to runtime settings
core/gallery/backend_types.go Added Version, URI, Digest fields to BackendMetadata
core/gallery/backends.go Record version/digest metadata on install
core/gallery/backends_version_test.go Tests for version-based backend selection
core/gallery/upgrade.go New upgrade detection and atomic upgrade execution
core/gallery/upgrade_test.go Tests for version comparison, atomic swap, rollback
core/http/endpoints/localai/backend.go New HTTP endpoints for upgrade operations
core/http/react-ui/src/pages/Backends.jsx Upgrade banner, "Upgrade All" button, filter toggles
core/http/react-ui/src/pages/Manage.jsx Per-backend upgrade buttons with version comparison
core/http/react-ui/src/pages/Settings.jsx Added auto_upgrade_backends setting
core/http/react-ui/src/utils/api.js New API methods for upgrade operations
core/http/react-ui/src/utils/config.js Config handling for auto-upgrade setting
core/http/routes/localai.go Route registration for upgrade endpoints
core/http/routes/ui_api.go Backend API route handlers
core/http/routes/ui_api_backends_test.go API tests for upgrade flow
core/services/advisorylock/keys.go Added KeyBackendUpgradeCheck advisory lock key
core/services/galleryop/backends.go Backend operations with upgrade support
core/services/galleryop/managers.go Extended BackendManager interface
core/services/galleryop/managers_local.go Local upgrade implementation
core/services/galleryop/operation.go Added Upgrade flag to ManagementOp
core/services/nodes/managers_distributed.go Distributed upgrade via NATS fan-out
pkg/oci/image.go Added GetImageDigest for cheap version checks
pkg/oci/image_test.go Tests for invalid image reference handling

Review Focus Areas

  • Advisory lock coordination in upgrade_checker.go prevents duplicate checks but verify edge cases around leader election failures
  • Atomic directory swap in upgrade.go handles rollback but check symlink behavior on different filesystems
  • Distributed upgrade fan-out in managers_distributed.go should verify NATS message handling for partial failures

Architecture

Design Decisions: Used PostgreSQL advisory locks for distributed coordination rather than external coordination services. This keeps infrastructure simple but requires PostgreSQL. Atomic directory swaps via symlinks provide safe rollback but assume atomic rename operations on the target filesystem.

Scalability & Extensibility: Upgrade checks run on a single leader in distributed mode, but on-demand checks and cache warming run everywhere for fast API response. This trades some consistency for availability. The BackendManager interface extension allows future upgrade strategies without changing callers.

Risks:

  • Intentional: Advisory locks require PostgreSQL; deployments without it will run checks on all instances (harmless but wasteful)
  • Intentional: GetImageDigest skips auth for public registries; private registry support may need extension
  • Unintentional: Distributed upgrade fan-out does not appear to aggregate worker failures back to the caller

Merge Status

NOT MERGEABLE — PR Score 14/100, below threshold (50)

  • [H4] PR quality score (14) is below merge floor (50)
  • [H5] 2 CRITICAL inline review findings need resolution
  • [H6] Code quality raw score (23) is below merge floor (40)
  • [H7] 2 HIGH-severity security findings

Comment on lines +119 to +121
func (uc *UpgradeChecker) Shutdown() {
close(uc.stop)
<-uc.done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Critical

Shutdown closes uc.stop unconditionally, so a second call panics; guard the close with sync.Once or a non-blocking state check.

Suggested fix
type UpgradeChecker struct {
	appConfig   *config.ApplicationConfig
	modelLoader *model.ModelLoader
	galleries   []config.Gallery
	systemState *system.SystemState
	db          *gorm.DB

	checkInterval time.Duration
	stop          chan struct{}
	done          chan struct{}
	triggerCh     chan struct{}
	shutdownOnce  sync.Once

	mu            sync.RWMutex
	lastUpgrades  map[string]gallery.UpgradeInfo
	lastCheckTime time.Time
}

func (uc *UpgradeChecker) Shutdown() {
	uc.shutdownOnce.Do(func() {
		close(uc.stop)
	})
	<-uc.done
}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 119-121
Issue Type: functional-critical
Severity: critical

Issue Description:
Shutdown closes uc.stop unconditionally, so a second call panics; guard the close with sync.Once or a non-blocking state check.

Current Code:
func (uc *UpgradeChecker) Shutdown() {
	close(uc.stop)
	<-uc.done
}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +82 to +95
go advisorylock.RunLeaderLoop(ctx, uc.db, advisorylock.KeyBackendUpgradeCheck, uc.checkInterval, func() {
uc.runCheck(ctx)
})

// Still listen for on-demand triggers (from API / settings change)
// and stop signal — these run on every instance.
for {
select {
case <-ctx.Done():
return
case <-uc.stop:
return
case <-uc.triggerCh:
uc.runCheck(ctx)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Critical

runCheck can execute concurrently from the local trigger path and the leader loop, causing overlapping upgrades of the same backend; serialize the whole check-and-upgrade routine with a dedicated mutex.

Suggested fix
type UpgradeChecker struct {
	appConfig   *config.ApplicationConfig
	modelLoader *model.ModelLoader
	galleries   []config.Gallery
	systemState *system.SystemState
	db          *gorm.DB

	checkInterval time.Duration
	stop          chan struct{}
	done          chan struct{}
	triggerCh     chan struct{}
	runMu         sync.Mutex

	mu            sync.RWMutex
	lastUpgrades  map[string]gallery.UpgradeInfo
	lastCheckTime time.Time
}

func (uc *UpgradeChecker) runCheck(ctx context.Context) {
	uc.runMu.Lock()
	defer uc.runMu.Unlock()

	upgrades, err := gallery.CheckBackendUpgrades(ctx, uc.galleries, uc.systemState)
	// existing logic...
}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 82-95
Issue Type: functional-critical
Severity: critical

Issue Description:
runCheck can execute concurrently from the local trigger path and the leader loop, causing overlapping upgrades of the same backend; serialize the whole check-and-upgrade routine with a dedicated mutex.

Current Code:
go advisorylock.RunLeaderLoop(ctx, uc.db, advisorylock.KeyBackendUpgradeCheck, uc.checkInterval, func() {
	uc.runCheck(ctx)
})

...
case <-uc.triggerCh:
	uc.runCheck(ctx)

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira


// distributedDB returns the PostgreSQL database for distributed coordination,
// or nil in standalone mode.
func (a *Application) distributedDB() *gorm.DB {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

In distributed mode the new upgrade checker is passed authDB instead of the distributed coordination DB, so starting two frontend replicas against separate auth databases will both acquire their own advisory locks and auto-upgrade the same backend concurrently.

Return the actual distributed coordination/postgres DB from a.distributed here (or rename/remove this helper if advisory locks are not supported in the current mode) so all replicas contend on the same database lock.

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/application.go
Lines: 91-91
Issue Type: functional-high
Severity: high

Issue Description:
In distributed mode the new upgrade checker is passed `authDB` instead of the distributed coordination DB, so starting two frontend replicas against separate auth databases will both acquire their own advisory locks and auto-upgrade the same backend concurrently.

Current Code:
func (a *Application) distributedDB() *gorm.DB {
	if a.distributed != nil {
		return a.authDB
	}
	return nil
}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment thread core/cli/backends.go
Comment on lines +75 to +76
// Check for upgrades
upgrades, _ := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness High

The error from CheckBackendUpgrades is discarded, so listing can silently hide upgrade-check failures; return or surface the error instead of ignoring it.

Suggested fix
	// Check for upgrades
	upgrades, err := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)
	if err != nil {
		return fmt.Errorf("failed to check backend upgrades: %w", err)
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/cli/backends.go
Lines: 75-76
Issue Type: robustness-high
Severity: high

Issue Description:
The error from CheckBackendUpgrades is discarded, so listing can silently hide upgrade-check failures; return or surface the error instead of ignoring it.

Current Code:
	// Check for upgrades
	upgrades, _ := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment thread core/gallery/backends.go
Comment on lines +288 to +294
// Record the OCI digest for upgrade detection (non-fatal on failure)
if uri.LooksLikeOCI() {
digest, digestErr := oci.GetImageDigest(string(uri), "", nil, nil)
if digestErr != nil {
xlog.Warn("Failed to get OCI image digest for backend", "uri", string(uri), "error", digestErr)
} else {
metadata.Digest = digest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security High

The digest is fetched by tag after installation, so a mutable OCI tag can change between download and metadata recording; capture and persist the exact resolved digest from the install step instead of re-resolving the tag here.

Suggested fix
	// Record the resolved OCI digest from the install step so mutable tags cannot drift.
	if uri.LooksLikeOCI() {
		resolvedDigest := downloadStatus.ResolvedDigest
		if resolvedDigest == "" {
			xlog.Warn("Resolved OCI digest unavailable after backend install", "uri", string(uri))
		} else {
			metadata.Digest = resolvedDigest
		}
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/gallery/backends.go
Lines: 288-294
Issue Type: security-high
Severity: high

Issue Description:
The digest is fetched by tag after installation, so a mutable OCI tag can change between download and metadata recording; capture and persist the exact resolved digest from the install step instead of re-resolving the tag here.

Current Code:
	// Record the OCI digest for upgrade detection (non-fatal on failure)
	if uri.LooksLikeOCI() {
		digest, digestErr := oci.GetImageDigest(string(uri), "", nil, nil)
		if digestErr != nil {
			xlog.Warn("Failed to get OCI image digest for backend", "uri", string(uri), "error", digestErr)
		} else {
			metadata.Digest = digest
		}
	}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +193 to +195
// Reuse install endpoint which will re-download the backend (force mode)
reply, err := d.adapter.InstallBackend(node.ID, name, "", string(galleriesJSON))
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

The upgrade path still passes an empty version/force argument to InstallBackend, so it does not guarantee a re-download; pass an explicit upgrade signal or call a dedicated upgrade endpoint.

Suggested fix
		// Use a dedicated upgrade call or pass an explicit force/version argument.
		reply, err := d.adapter.InstallBackend(node.ID, name, "force", string(galleriesJSON))
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/services/nodes/managers_distributed.go
Lines: 193-195
Issue Type: functional-high
Severity: high

Issue Description:
The upgrade path still passes an empty version/force argument to InstallBackend, so it does not guarantee a re-download; pass an explicit upgrade signal or call a dedicated upgrade endpoint.

Current Code:
		// Reuse install endpoint which will re-download the backend (force mode)
		reply, err := d.adapter.InstallBackend(node.ID, name, "", string(galleriesJSON))

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +338 to +339
if settings.AutoUpgradeBackends != nil {
appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security Medium

This setting bypasses the existing env var precedence checks, so a writable runtime_settings.json can override startup policy; gate it behind the corresponding env flag like the surrounding settings.

Suggested fix
			if settings.AutoUpgradeBackends != nil && !envAutoUpgradeBackends {
				appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends
			}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert go developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/config_file_watcher.go
Lines: 338-339
Issue Type: security-medium
Severity: medium

Issue Description:
This setting bypasses the existing env var precedence checks, so a writable runtime_settings.json can override startup policy; gate it behind the corresponding env flag like the surrounding settings.

Current Code:
			if settings.AutoUpgradeBackends != nil {
				appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends
			}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +46 to +49
if (!preferDevLoaded && data?.preferDevelopmentBackends) {
setShowDevelopment(true)
setPreferDevLoaded(true)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

The server preference flag is only marked as loaded when the preference is true, so a false value causes this branch to re-run on every fetch; set the loaded flag regardless after the first response.

Suggested fix
      if (!preferDevLoaded) {
        setShowDevelopment(!!data?.preferDevelopmentBackends)
        setPreferDevLoaded(true)
      }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert javascript developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/react-ui/src/pages/Backends.jsx
Lines: 46-49
Issue Type: functional-medium
Severity: medium

Issue Description:
The server preference flag is only marked as loaded when the preference is true, so a false value causes this branch to re-run on every fetch; set the loaded flag regardless after the first response.

Current Code:
      if (!preferDevLoaded && data?.preferDevelopmentBackends) {
        setShowDevelopment(true)
        setPreferDevLoaded(true)
      }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +68 to +70
backendsApi.checkUpgrades()
.then(data => setUpgrades(data || {}))
.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.

Robustness Medium

The upgrades refresh silently swallows errors, so stale update data can persist indefinitely; clear the upgrades state on failure.

Suggested fix
    backendsApi.checkUpgrades()
      .then(data => setUpgrades(data || {}))
      .catch(() => setUpgrades({}))
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert javascript developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/react-ui/src/pages/Backends.jsx
Lines: 68-70
Issue Type: robustness-medium
Severity: medium

Issue Description:
The upgrades refresh silently swallows errors, so stale update data can persist indefinitely; clear the upgrades state on failure.

Current Code:
    backendsApi.checkUpgrades()
      .then(data => setUpgrades(data || {}))
      .catch(() => {})

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

deleteInstalledBackend: (name) => `/api/backends/system/delete/${name}`,
backendsUpgrades: '/api/backends/upgrades',
backendsUpgradesCheck: '/api/backends/upgrades/check',
upgradeBackend: (name) => `/api/backends/upgrade/${name}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security Medium

The backend name is interpolated into the path without URL encoding, so names containing slashes or reserved characters can hit the wrong endpoint; wrap the parameter with encodeURIComponent.

Suggested change
upgradeBackend: (name) => `/api/backends/upgrade/${name}`,
upgradeBackend: (name) => `/api/backends/upgrade/${encodeURIComponent(name)}`,
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert javascript developer with deep knowledge of security, performance, and best practices.

### Context

File: core/http/react-ui/src/utils/config.js
Lines: 28-28
Issue Type: security-medium
Severity: medium

Issue Description:
The backend name is interpolated into the path without URL encoding, so names containing slashes or reserved characters can hit the wrong endpoint; wrap the parameter with encodeURIComponent.

Current Code:
    upgradeBackend: (name) => `/api/backends/upgrade/${name}`,

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Medium
Files Scanned 31

Consider addressing security findings before merging

Scan completed in 55.8s

View vulnerability details (2 items)

1. MATH_RANDOM_USED (CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)) HIGH

File: core/application/application.go (line 5)
Category: Other

Do not use math/rand. Use crypto/rand instead.

Fix: Review and fix the security issue following OWASP guidelines.


2. REACT_DANGEROUSLYSET (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) HIGH

File: core/http/react-ui/src/pages/Backends.jsx (line 576)
Category: Other

Detection of dangerouslySetInnerHTML from non-constant definition. This can inadvertently expose users to cross-site scripting (XSS) attacks if this comes from user-provided input. If you have to use dangerouslySetInnerHTML, consider using a sanitization library such as DOMPurify to sanitize your HTML.

Fix: Review and fix the security issue following OWASP guidelines.


Security scan powered by Codity.ai

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 12
Scanner npm audit
Top 10 Vulnerabilities (12 total found)

1. @hono/node-server various

CVE: GHSA-92pp-h63x-v22m
Severity: MODERATE
Fixed in: True

@hono/node-server: Middleware bypass via repeated slashes in serveStatic


2. brace-expansion various

CVE: GHSA-f886-m6hf-6m8v
Severity: MODERATE
Fixed in: True

brace-expansion: Zero-step sequence causes process hang and memory exhaustion


3. dompurify various

CVE: GHSA-39q2-94rc-95cp, GHSA-h7mw-gpvr-xq4m, GHSA-crv5-9vww-q3g8, GHSA-v9jr-rg53-9pgp
Severity: MODERATE
Fixed in: True

DOMPurify's ADD_TAGS function form bypasses FORBID_TAGS due to short-circuit evaluation


4. express-rate-limit various

CVE: N/A
Severity: MODERATE
Fixed in: True

Vulnerability in express-rate-limit


5. fast-uri various

CVE: GHSA-q3j6-qgpj-74h6, GHSA-v39h-62p7-jpjc
Severity: HIGH
Fixed in: True

fast-uri vulnerable to path traversal via percent-encoded dot segments


6. flatted various

CVE: GHSA-25h7-pfq9-p65f, GHSA-rf6f-7fwh-wjgh
Severity: HIGH
Fixed in: True

flatted vulnerable to unbounded recursion DoS in parse() revive phase


7. hono various

CVE: GHSA-26pp-8wgv-hjvm, GHSA-r5rp-j6wh-rvv4, GHSA-xf4j-xp2r-rqqx, GHSA-wmmm-f939-6g9c, GHSA-458j-xx4x-4375, GHSA-xpcf-pg52-r92g, GHSA-qp7p-654g-cw7p, GHSA-hm8q-7f3q-5f36, GHSA-p77w-8qqv-26rm, GHSA-9vqf-7f2p-gf9v, GHSA-69xw-7hcm-h432
Severity: MODERATE
Fixed in: True

Hono missing validation of cookie name on write path in setCookie()


8. ip-address various

CVE: GHSA-v2v4-37r5-5v8g
Severity: MODERATE
Fixed in: True

ip-address has XSS in Address6 HTML-emitting methods


9. path-to-regexp various

CVE: GHSA-j3q9-mxjg-w52f, GHSA-27v5-c462-wpq7
Severity: HIGH
Fixed in: True

path-to-regexp vulnerable to Denial of Service via sequential optional groups


10. picomatch various

CVE: GHSA-3v7f-55p6-f55p, GHSA-c2c7-rcm5-vvqj
Severity: HIGH
Fixed in: True

Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching


2 more vulnerabilities not shown. Update dependencies to fix these issues.

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 4
Scanner pip-audit
View vulnerability details (4 items)

1. pip 24.0

CVE: GHSA-4xh5-x5gv-qwph
Fixed in: 25.3

When extracting a tar archive pip may not check symbolic links point into the extraction directory if the tarfile module doesn't implement PEP 706. Note that upgrading pip to a "fixed" version for thi


2. pip 24.0

CVE: GHSA-6vgw-5pg2-w6jp
Fixed in: 26.0

When pip is installing and extracting a maliciously crafted wheel archive, files may be extracted outside the installation directory. The path traversal is limited to prefixes of the installation dire


3. pip 24.0

CVE: GHSA-58qw-9mgm-455v
Fixed in:

pip handles concatenated tar and ZIP files as ZIP files regardless of filename or whether a file is both a tar and ZIP file. This behavior could result in confusing installation behavior, such as inst


4. pip 24.0

CVE: GHSA-jp4c-xjxw-mgf9
Fixed in: 26.1

pip prior to version 26.1 would run self-update check functionality after installing wheel files which required importing well-known Python modules names. These module imports were intentionally defer

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 758
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 6
Low Risk (Permissive) 659
Unknown License 93

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 6 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.2

MPL-2.0 (5 packages):

  • github.com/philippgille/chromem-go 0.7.0
  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 93 packages
  • github.com/cloudflare/circl 1.6.1
  • github.com/go-git/gcfg 1.5.1-0.20230307220236-3a3c6141e376
  • github.com/eritikass/githubmarkdownconvertergo 0.1.10
  • github.com/go-git/go-billy/v5 5.6.2
  • github.com/go-git/go-git/v5 5.16.4
  • github.com/gocolly/colly 1.2.0
  • github.com/golang/protobuf 1.5.4
  • github.com/google/go-github/v69 69.2.0
  • github.com/google/go-querystring 1.1.0
  • github.com/kennygrant/sanitize 1.2.4
  • github.com/mschoch/smat 0.2.0
  • github.com/mudler/skillserver 0.0.6
  • github.com/olekukonko/tablewriter 0.0.5
  • github.com/pjbgf/sha1cd 0.3.2
  • github.com/saintfish/chardet 0.0.0-20230101081208-5e3ef4b5456d
  • github.com/segmentio/asm 1.1.3
  • github.com/sergi/go-diff 1.4.0
  • github.com/skeema/knownhosts 1.3.1
  • go.mau.fi/util 0.3.0
  • go.starlark.net 0.0.0-20250417143717-f57e51f710eb

...and 73 more

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 18, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/LocalAI · PR #6

Scanned: 2026-05-18 20:13 UTC | Score: 23/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 2
Medium 3
Low 104
Top Findings

[CQ-LLM-001] core/application/upgrade_checker.go:198 (Complexity · HIGH)

Issue: The 'Run' method in UpgradeChecker has multiple nested loops and conditionals, increasing cyclomatic complexity.
Suggestion: Consider refactoring the 'Run' method to reduce nesting and improve readability.

for {
    select {
    case <-ctx.Done():
        return
    case <-uc.stop:
        return
    case <-ticker.C:
        uc.runCheck(ctx)
    case <-uc.triggerCh:
        uc.runCheck(ctx)
    }
}

[CQ-LLM-003] core/cli/backends.go:134 (Error_Handling · HIGH)

Issue: The error from 'CheckBackendUpgrades' is ignored, which can lead to unhandled errors.
Suggestion: Handle the error appropriately to ensure that the application can respond to failures.

upgrades, _ := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

[CQ-LLM-006] core/application/upgrade_checker.go:1 (Testability · MEDIUM)

Issue: The UpgradeChecker has hard-coded dependencies, making it difficult to test in isolation.
Suggestion: Introduce dependency injection for the database and other dependencies to improve testability.

func NewUpgradeChecker(appConfig *config.ApplicationConfig, ml *model.ModelLoader, db *gorm.DB) *UpgradeChecker {

[CQ-LLM-002] core/cli/backends.go:134 (Duplication · MEDIUM)

Issue: The logic for printing backend information is duplicated in multiple places.
Suggestion: Extract the printing logic into a separate function to adhere to DRY principles.

fmt.Printf(" * %s@%s%s (installed, upgrade available: %s)\n", backend.Gallery.Name, backend.Name, versionStr, upgradeStr)

[CQ-LLM-004] core/cli/backends.go:134 (Performance · MEDIUM)

Issue: Calling 'CheckBackendUpgrades' with a new context may lead to performance issues if called frequently.
Suggestion: Consider reusing the context or optimizing the upgrade check to avoid unnecessary calls.

upgrades, err := gallery.CheckBackendUpgrades(context.Background(), galleries, systemState)

[CQ-LLM-005] core/application/upgrade_checker.go:1 (Documentation · LOW)

Issue: Missing documentation for the UpgradeChecker struct and its methods.
Suggestion: Add docstrings to the UpgradeChecker struct and its methods to improve code documentation.

type UpgradeChecker struct {

[CQ-LLM-007] core/application/upgrade_checker.go:1 (Maintainability · LOW)

Issue: Magic number '6 * time.Hour' is used for checkInterval without explanation.
Suggestion: Define a constant for the check interval to improve maintainability and readability.

checkInterval: 6 * time.Hour,

[CQ-008] core/application/upgrade_checker.go:73 (Maintainability · LOW)

Issue: Magic number 30 in code
Suggestion: Extract to a named constant

case <-time.After(30 * time.Second):

[CQ-009] core/cli/backends.go:89 (Style · LOW)

Issue: Line exceeds 120 characters (125 chars)
Suggestion: Break long lines into multiple lines for readability

				fmt.Printf(" * %s@%s%s (installed, upgrade available: %s)\n", backend.Gallery.Name, backend.Name, versionStr, upgrad...

[CQ-008] core/cli/backends.go:194 (Maintainability · LOW)

Issue: Magic number 10 in code
Suggestion: Extract to a named constant

v := int(percentage * 10)

Per-File Breakdown

File Critical High Medium Low Total
core/application/upgrade_checker.go 0 1 1 3 5
core/cli/backends.go 0 1 2 3 6
core/cli/run.go 0 0 0 1 1
core/config/runtime_settings.go 0 0 0 1 1
core/gallery/backends_version_test.go 0 0 0 3 3
core/gallery/upgrade.go 0 0 0 4 4
core/gallery/upgrade_test.go 0 0 0 6 6
core/http/endpoints/localai/backend.go 0 0 0 7 7
core/http/react-ui/src/pages/Backends.jsx 0 0 0 38 38
core/http/react-ui/src/pages/Manage.jsx 0 0 0 20 20
core/http/react-ui/src/pages/Settings.jsx 0 0 0 1 1
core/http/routes/ui_api.go 0 0 0 5 5
core/http/routes/ui_api_backends_test.go 0 0 0 8 8
core/services/advisorylock/keys.go 0 0 0 2 2
core/services/nodes/managers_distributed.go 0 0 0 1 1
pkg/oci/image.go 0 0 0 1 1

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@chay2199

chay2199 commented Jun 3, 2026

Copy link
Copy Markdown

@codity review

@codity-chait

codity-chait Bot commented Jun 3, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added automatic backend upgrade system with version tracking, manual CLI commands, and UI controls.
  • Implemented distributed-safe upgrade checking using PostgreSQL advisory locks to prevent duplicate checks across frontend instances.
  • Added atomic upgrade operations with automatic rollback on failure using directory swaps.

Key Changes by Area

Backend Management: New UpgradeChecker service runs periodic checks every 6 hours with optional auto-upgrade. BackendManager interface extended with UpgradeBackend() and CheckUpgrades() methods.

CLI: New backends upgrade command with progress bars and selective targeting. Enhanced backends list shows available upgrades and version info.

API: Three new endpoints for upgrade operations (/backends/upgrades, /backends/upgrades/check, /backends/upgrade/:name).

UI: Upgrade banners with "Upgrade All" button, version-aware filtering, and individual backend upgrade/reinstall controls in React components.

Configuration: New AutoUpgradeBackends runtime setting with live reload support via AUTO_UPGRADE_BACKENDS env var.

Files Changed

File Changes Summary
core/application/upgrade_checker.go New background service for periodic upgrade checks with distributed locking
core/application/startup.go Initializes UpgradeChecker with 30-second startup delay
core/cli/backends.go New upgrade command and enhanced list output
core/cli/run.go Auto-upgrade flag registration
core/config/application_config.go AutoUpgradeBackends configuration field
core/config/runtime_settings.go Runtime setting for live config changes
core/config/application_config_test.go Test coverage for auto_upgrade_backends settings
core/gallery/backend_types.go Added Version, URI, Digest fields to BackendMetadata
core/gallery/backends.go Metadata recording in InstallBackend
core/gallery/upgrade.go CheckBackendUpgrades() and atomic UpgradeBackend() with rollback
core/gallery/upgrade_test.go Test coverage for upgrade detection and atomic swaps
core/gallery/backends_version_test.go Version recording tests
core/http/endpoints/localai/backend.go HTTP endpoints for upgrade operations
core/http/react-ui/src/pages/Backends.jsx Upgrade banner, filtering, version display
core/http/react-ui/src/pages/Manage.jsx Individual upgrade/reinstall buttons
core/http/react-ui/src/pages/Settings.jsx Auto-upgrade setting toggle
core/http/react-ui/src/utils/api.js API client methods for upgrade endpoints
core/http/react-ui/src/utils/config.js Config utilities for upgrade settings
core/http/routes/localai.go Route registration for upgrade endpoints
core/http/routes/ui_api.go UI API route wiring
core/http/routes/ui_api_backends_test.go Integration tests for upgrade flow
core/services/advisorylock/keys.go KeyBackendUpgradeCheck advisory lock key
core/services/galleryop/backends.go ManagementOp with Upgrade flag
core/services/galleryop/managers.go BackendManager interface extensions
core/services/galleryop/managers_local.go Local upgrade implementation
core/services/galleryop/operation.go Upgrade flag in ManagementOp
core/services/nodes/managers_distributed.go Distributed upgrade via NATS fanout
pkg/oci/image.go GetImageDigest() for efficient OCI metadata checks
pkg/oci/image_test.go Digest fetch error handling tests

Review Focus Areas

  • Distributed locking in upgrade_checker.go: Verify PostgreSQL advisory lock prevents duplicate checks without deadlocking on restart.
  • Atomic directory swap rollback in upgrade.go:112-237: Confirm failure scenarios properly restore backups.
  • NATS fanout error handling in managers_distributed.go:180-214: Check partial failure behavior when some nodes fail to upgrade.

Architecture

Design Decisions:

  • 6-hour check interval balances freshness vs. registry load. 30-second startup delay prevents blocking application initialization.
  • OCI digest comparison preferred over version strings for immutable identity. Version strings displayed for user clarity.
  • Atomic directory swaps (rename, not copy) chosen for instant rollback capability. Tradeoff: requires same filesystem for backup/destination.

Scalability & Extensibility:

  • Upgrade checking is single-writer via advisory locks; out of scope to shard by backend type. Gallery comparison remains global (not per-node) by design.
  • UpgradeInfoProvider interface allows alternative upgrade sources beyond gallery metadata.

Risks:

  • Intentional: Auto-upgrade disabled by default to prevent unexpected breaking changes. Users must opt in.
  • Intentional: Distributed upgrade fans out to all healthy nodes; partial failures leave cluster in mixed-version state. Operator must monitor and retry.
  • Unintentional: Advisory lock timeout not implemented; stuck lock could block checks indefinitely until instance restart.

Merge Status

NOT MERGEABLE — PR Score 36/100, below threshold (50)

  • [H4] PR quality score (36) is below merge floor (50)
  • [H6] Code quality raw score (23) is below merge floor (40)
  • [H7] 2 HIGH-severity security findings

@codity-chait

codity-chait Bot commented Jun 3, 2026

Copy link
Copy Markdown

Workflow Diagrams

Automatically generated sequence diagrams showing the workflows in this PR

1. Backend Upgrade Service Workflow

Complex complexity • Components: UpgradeChecker, Application, BackendsUpgrade CLI command

sequenceDiagram
    title Backend Upgrade Checker Workflow

    participant Startup as ApplicationStartup
    participant UC as UpgradeChecker
    participant AL as AdvisoryLock
    participant Gallery as GalleryService
    participant DB as PostgreSQL
    participant API as HTTPAPI
    participant Config as ConfigWatcher

    Startup->>UC: NewUpgradeChecker(appConfig, modelLoader, db)
    Note over Startup,UC: db is nil in standalone mode, or PostgreSQL connection in distributed mode

    Startup->>UC: Run(ctx) as goroutine

    UC->>UC: Wait 30s initial delay

    UC->>Gallery: CheckBackendUpgrades(galleries, systemState)
    Gallery-->>UC: map[string]UpgradeInfo

    UC->>UC: Cache results with RWMutex

    alt Distributed Mode (db != nil)
        UC->>AL: RunLeaderLoop(ctx, db, key, interval, callback)
        loop Every 6 hours
            AL->>DB: Acquire advisory lock
            alt Lock acquired (leader elected)
                AL->>UC: Execute callback: runCheck(ctx)
                UC->>Gallery: CheckBackendUpgrades
                Gallery-->>UC: upgrades
                UC->>UC: Update cache

                opt AutoUpgradeBackends enabled
                    UC->>Gallery: UpgradeBackend(ctx, systemState, modelLoader, galleries, name, nil)
                    Gallery-->>UC: success/error
                    UC->>Gallery: CheckBackendUpgrades (recheck after upgrade)
                end
            else Lock not acquired
                Note over AL: Skip check, another instance is leader
            end
        end

        UC->>UC: Listen for triggerCh (on-demand checks)
    else Standalone Mode (db == nil)
        UC->>UC: Start ticker (6h interval)
        loop Ticker fires or triggerCh received
            UC->>UC: runCheck(ctx)
            UC->>Gallery: CheckBackendUpgrades
            Gallery-->>UC: upgrades
            UC->>UC: Update cache

            opt AutoUpgradeBackends enabled
                UC->>Gallery: UpgradeBackend
            end
        end
    end

    API->>UC: GetAvailableUpgrades()
    UC->>UC: RLock, copy cache
    UC-->>API: map[string]UpgradeInfo

    API->>UC: TriggerCheck()
    UC->>UC: Send to triggerCh (non-blocking)

    Config->>UC: AutoUpgradeBackends setting changed
    Note over Config: Applied via runtime_settings.json or env vars
    Config->>UC: TriggerCheck() (immediate re-check)

    Startup->>UC: Shutdown()
    UC->>UC: Close stop channel
    UC-->>Startup: Wait for done channel
Loading

Note: Diagrams show detected patterns only. Complex workflows may require manual review.

Comment thread core/cli/backends.go
Comment on lines +139 to +141
if err := json.Unmarshal([]byte(bu.BackendGalleries), &galleries); err != nil {
xlog.Error("unable to load galleries", "error", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

JSON unmarshal error is logged and silently swallowed in BackendsUpgrade.Run. Execution continues with an empty galleries slice, causing CheckBackendUpgrades to return zero results. The command then prints "All backends are up to date." even when the gallery configuration is malformed: the user gets a misleading success message instead of an actionable error. The identical pattern in the pre-existing BackendsList.Run and BackendsInstall.Run should also be fixed, but this is a new, user-facing subcommand that makes the silent failure especially harmful.

Suggested fix
	if err := json.Unmarshal([]byte(bu.BackendGalleries), &galleries); err != nil {
		return fmt.Errorf("unable to load galleries: %w", err)
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/cli/backends.go
Lines: 139-141
Issue Type: functional-high
Severity: high

Issue Description:
JSON unmarshal error is logged and silently swallowed in `BackendsUpgrade.Run`. Execution continues with an empty `galleries` slice, causing `CheckBackendUpgrades` to return zero results. The command then prints "All backends are up to date." even when the gallery configuration is malformed: the user gets a misleading success message instead of an actionable error. The identical pattern in the pre-existing `BackendsList.Run` and `BackendsInstall.Run` should also be fixed, but this is a new, user-facing subcommand that makes the silent failure especially harmful.

Current Code:
	if err := json.Unmarshal([]byte(bu.BackendGalleries), &galleries); err != nil {
		xlog.Error("unable to load galleries", "error", err)
	}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/gallery/upgrade.go
Comment on lines +129 to +132
if installed.Metadata != nil && installed.Metadata.MetaBackendFor != "" {
xlog.Info("Meta backend detected, upgrading concrete backend", "meta", backendName, "concrete", installed.Metadata.MetaBackendFor)
return UpgradeBackend(ctx, systemState, modelLoader, galleries, installed.Metadata.MetaBackendFor, downloadStatus)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness High

Unbounded recursion in UpgradeBackend when resolving meta-backends. If a metadata.json file sets MetaBackendFor to the backend's own name (or a chain A→B→A forms a cycle), UpgradeBackend recurses without any depth limit or visited-set guard until the goroutine stack is exhausted, crashing the process. A malformed or manually edited metadata file in the backends directory is sufficient to trigger this. The self-reference case is the easiest to guard against.

Suggested fix
	// If this is a meta backend, recursively upgrade the concrete backend it points to
	if installed.Metadata != nil && installed.Metadata.MetaBackendFor != "" {
		if installed.Metadata.MetaBackendFor == backendName {
			return fmt.Errorf("meta backend %q has a self-referential MetaBackendFor; refusing to recurse", backendName)
		}
		xlog.Info("Meta backend detected, upgrading concrete backend", "meta", backendName, "concrete", installed.Metadata.MetaBackendFor)
		return UpgradeBackend(ctx, systemState, modelLoader, galleries, installed.Metadata.MetaBackendFor, downloadStatus)
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/gallery/upgrade.go
Lines: 129-132
Issue Type: robustness-high
Severity: high

Issue Description:
Unbounded recursion in `UpgradeBackend` when resolving meta-backends. If a `metadata.json` file sets `MetaBackendFor` to the backend's own name (or a chain A→B→A forms a cycle), `UpgradeBackend` recurses without any depth limit or visited-set guard until the goroutine stack is exhausted, crashing the process. A malformed or manually edited metadata file in the backends directory is sufficient to trigger this. The self-reference case is the easiest to guard against.

Current Code:
	// If this is a meta backend, recursively upgrade the concrete backend it points to
	if installed.Metadata != nil && installed.Metadata.MetaBackendFor != "" {
		xlog.Info("Meta backend detected, upgrading concrete backend", "meta", backendName, "concrete", installed.Metadata.MetaBackendFor)
		return UpgradeBackend(ctx, systemState, modelLoader, galleries, installed.Metadata.MetaBackendFor, downloadStatus)
	}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment thread core/gallery/upgrade.go
Comment on lines +173 to +175
if _, err := os.Stat(newRunFile); os.IsNotExist(err) {
os.RemoveAll(tmpPath)
return fmt.Errorf("upgrade validation failed: run.sh not found in new backend")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

The validation check only handles os.IsNotExist. Any other error from os.Stat (e.g., permission denied, I/O error, path-too-long) is silently ignored and treated as "run.sh is present". The code then continues with the atomic swap, potentially installing an inaccessible or corrupt backend while the old installation has already been moved to .backup. The fix is to treat any non-nil stat error as a validation failure.

Suggested fix
	if _, statErr := os.Stat(newRunFile); statErr != nil {
		os.RemoveAll(tmpPath)
		return fmt.Errorf("upgrade validation failed: run.sh not accessible in new backend: %w", statErr)
	}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/gallery/upgrade.go
Lines: 173-175
Issue Type: robustness-medium
Severity: medium

Issue Description:
The validation check only handles `os.IsNotExist`. Any other error from `os.Stat` (e.g., permission denied, I/O error, path-too-long) is silently ignored and treated as "run.sh is present". The code then continues with the atomic swap, potentially installing an inaccessible or corrupt backend while the old installation has already been moved to `.backup`. The fix is to treat any non-nil stat error as a validation failure.

Current Code:
	if _, err := os.Stat(newRunFile); os.IsNotExist(err) {
		os.RemoveAll(tmpPath)
		return fmt.Errorf("upgrade validation failed: run.sh not found in new backend")
	}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +119 to +122
func (uc *UpgradeChecker) Shutdown() {
close(uc.stop)
<-uc.done
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

Shutdown() calls close(uc.stop) without any guard against a second call. Closing an already-closed channel panics in Go. If Shutdown() is ever called twice: e.g., via a deferred call and an explicit call in an error path, or in tests: the process crashes. sync.Once is the idiomatic fix; requires adding a stopOnce sync.Once field to UpgradeChecker.

Suggested fix
// Shutdown stops the upgrade checker loop. Safe to call more than once.
func (uc *UpgradeChecker) Shutdown() {
	uc.stopOnce.Do(func() { close(uc.stop) })
	<-uc.done
}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 119-122
Issue Type: robustness-medium
Severity: medium

Issue Description:
`Shutdown()` calls `close(uc.stop)` without any guard against a second call. Closing an already-closed channel panics in Go. If `Shutdown()` is ever called twice: e.g., via a deferred call and an explicit call in an error path, or in tests: the process crashes. `sync.Once` is the idiomatic fix; requires adding a `stopOnce sync.Once` field to `UpgradeChecker`.

Current Code:
// Shutdown stops the upgrade checker loop.
func (uc *UpgradeChecker) Shutdown() {
	close(uc.stop)
	<-uc.done
}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +82 to +84
go advisorylock.RunLeaderLoop(ctx, uc.db, advisorylock.KeyBackendUpgradeCheck, uc.checkInterval, func() {
uc.runCheck(ctx)
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

In distributed mode, Run() spawns a RunLeaderLoop goroutine whose only cancellation signal is ctx. When Shutdown() is called (closes uc.stop), the outer for loop returns and close(uc.done) is executed: but the spawned goroutine keeps running until the parent context is cancelled. Any caller (including tests) that invokes Shutdown() before cancelling ctx incorrectly believes all activity has stopped, while the goroutine continues to hold the advisory lock and run runCheck. Wrapping ctx in a derived cancellable context and cancelling it via defer in Run() would propagate the stop signal to the goroutine.

Suggested fix
		runCtx, cancelRun := context.WithCancel(ctx)
		defer cancelRun()
		go advisorylock.RunLeaderLoop(runCtx, uc.db, advisorylock.KeyBackendUpgradeCheck, uc.checkInterval, func() {
			uc.runCheck(runCtx)
		})
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 82-84
Issue Type: robustness-medium
Severity: medium

Issue Description:
In distributed mode, `Run()` spawns a `RunLeaderLoop` goroutine whose only cancellation signal is `ctx`. When `Shutdown()` is called (closes `uc.stop`), the outer `for` loop returns and `close(uc.done)` is executed: but the spawned goroutine keeps running until the parent context is cancelled. Any caller (including tests) that invokes `Shutdown()` before cancelling `ctx` incorrectly believes all activity has stopped, while the goroutine continues to hold the advisory lock and run `runCheck`. Wrapping `ctx` in a derived cancellable context and cancelling it via `defer` in `Run()` would propagate the stop signal to the goroutine.

Current Code:
		go advisorylock.RunLeaderLoop(ctx, uc.db, advisorylock.KeyBackendUpgradeCheck, uc.checkInterval, func() {
			uc.runCheck(ctx)
		})

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@codity-chait

codity-chait Bot commented Jun 3, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Medium
Files Scanned 31

Consider addressing security findings before merging

Scan completed in 68.8s

View vulnerability details (2 items)

1. MATH_RANDOM_USED (CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)) HIGH

File: core/application/application.go (line 5)
Category: Other

Do not use math/rand. Use crypto/rand instead.

Fix: Review and fix the security issue following OWASP guidelines.


2. REACT_DANGEROUSLYSET (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) HIGH

File: core/http/react-ui/src/pages/Backends.jsx (line 576)
Category: Other

Detection of dangerouslySetInnerHTML from non-constant definition. This can inadvertently expose users to cross-site scripting (XSS) attacks if this comes from user-provided input. If you have to use dangerouslySetInnerHTML, consider using a sanitization library such as DOMPurify to sanitize your HTML.

Fix: Review and fix the security issue following OWASP guidelines.


Security scan powered by Codity.ai

@codity-chait

codity-chait Bot commented Jun 3, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 758
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 6
Low Risk (Permissive) 659
Unknown License 93

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 6 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.2

MPL-2.0 (5 packages):

  • github.com/philippgille/chromem-go 0.7.0
  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 93 packages
  • github.com/dslipak/pdf 0.0.2
  • github.com/eritikass/githubmarkdownconvertergo 0.1.10
  • github.com/go-git/gcfg 1.5.1-0.20230307220236-3a3c6141e376
  • github.com/go-git/go-billy/v5 5.6.2
  • github.com/go-git/go-git/v5 5.16.4
  • github.com/gocolly/colly 1.2.0
  • github.com/golang/protobuf 1.5.4
  • github.com/google/go-querystring 1.1.0
  • github.com/google/go-github/v69 69.2.0
  • github.com/kennygrant/sanitize 1.2.4
  • github.com/mschoch/smat 0.2.0
  • github.com/mudler/skillserver 0.0.6
  • github.com/olekukonko/tablewriter 0.0.5
  • github.com/pjbgf/sha1cd 0.3.2
  • github.com/saintfish/chardet 0.0.0-20230101081208-5e3ef4b5456d
  • github.com/segmentio/asm 1.1.3
  • github.com/sergi/go-diff 1.4.0
  • github.com/skeema/knownhosts 1.3.1
  • go.mau.fi/util 0.3.0
  • go.starlark.net 0.0.0-20250417143717-f57e51f710eb

...and 73 more

Powered by Codity.ai · Docs

@codity-chait

codity-chait Bot commented Jun 3, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/LocalAI · PR #6

Scanned: 2026-06-03 23:11 UTC | Score: 23/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 2
Medium 3
Low 103
Top Findings

[CQ-LLM-001] core/application/upgrade_checker.go:198 (Complexity · HIGH)

Issue: The 'Run' method in UpgradeChecker has deep nesting due to multiple select statements and if conditions.
Suggestion: Consider refactoring the 'Run' method to reduce nesting and improve readability.

if uc.db != nil { ... } else { ... }

[CQ-LLM-006] core/cli/backends.go:134 (Maintainability · HIGH)

Issue: The method 'Run' in BackendsUpgrade contains hard-coded strings and magic numbers.
Suggestion: Define constants for hard-coded strings and magic numbers to improve maintainability.

fmt.Printf("Backend %s: no upgrade available
", name)

[CQ-LLM-002] core/application/upgrade_checker.go:164 (Error_Handling · MEDIUM)

Issue: The error from 'gallery.CheckBackendUpgrades' is swallowed without proper handling.
Suggestion: Log the error or handle it appropriately instead of ignoring it.

upgrades, err := gallery.CheckBackendUpgrades(ctx, uc.galleries, uc.systemState)

[CQ-LLM-003] core/application/upgrade_checker.go:164 (Performance · MEDIUM)

Issue: The method 'runCheck' performs a check for upgrades and then immediately checks again after upgrades, which could lead to unnecessary database calls.
Suggestion: Consider caching the results of the first check and only re-checking if necessary.

if freshUpgrades, err := gallery.CheckBackendUpgrades(ctx, uc.galleries, uc.systemState); err == nil { ... }

[CQ-LLM-005] core/cli/backends.go:134 (Testability · MEDIUM)

Issue: The 'Run' method in BackendsUpgrade directly uses global state and does not allow for dependency injection.
Suggestion: Refactor the method to accept dependencies as parameters to improve testability.

systemState, err := system.GetSystemState(...)

[CQ-LLM-004] core/application/upgrade_checker.go:1 (Documentation · LOW)

Issue: The UpgradeChecker struct lacks a detailed docstring explaining its purpose and usage.
Suggestion: Add a comprehensive docstring to the UpgradeChecker struct to improve documentation.

type UpgradeChecker struct { ... }

[CQ-008] core/application/upgrade_checker.go:73 (Maintainability · LOW)

Issue: Magic number 30 in code
Suggestion: Extract to a named constant

case <-time.After(30 * time.Second):

[CQ-009] core/cli/backends.go:89 (Style · LOW)

Issue: Line exceeds 120 characters (125 chars)
Suggestion: Break long lines into multiple lines for readability

				fmt.Printf(" * %s@%s%s (installed, upgrade available: %s)\n", backend.Gallery.Name, backend.Name, versionStr, upgrad...

[CQ-008] core/cli/backends.go:194 (Maintainability · LOW)

Issue: Magic number 10 in code
Suggestion: Extract to a named constant

v := int(percentage * 10)

[CQ-009] core/cli/backends.go:200 (Style · LOW)

Issue: Line exceeds 120 characters (131 chars)
Suggestion: Break long lines into multiple lines for readability

		if err := gallery.UpgradeBackend(context.Background(), systemState, modelLoader, galleries, name, progressCallback); e...

Per-File Breakdown

File Critical High Medium Low Total
core/application/upgrade_checker.go 0 1 2 2 5
core/cli/backends.go 0 1 1 3 5
core/cli/run.go 0 0 0 1 1
core/config/runtime_settings.go 0 0 0 1 1
core/gallery/backends_version_test.go 0 0 0 3 3
core/gallery/upgrade.go 0 0 0 4 4
core/gallery/upgrade_test.go 0 0 0 6 6
core/http/endpoints/localai/backend.go 0 0 0 7 7
core/http/react-ui/src/pages/Backends.jsx 0 0 0 38 38
core/http/react-ui/src/pages/Manage.jsx 0 0 0 20 20
core/http/react-ui/src/pages/Settings.jsx 0 0 0 1 1
core/http/routes/ui_api.go 0 0 0 5 5
core/http/routes/ui_api_backends_test.go 0 0 0 8 8
core/services/advisorylock/keys.go 0 0 0 2 2
core/services/nodes/managers_distributed.go 0 0 0 1 1
pkg/oci/image.go 0 0 0 1 1

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@chay2199

chay2199 commented Jun 8, 2026

Copy link
Copy Markdown

@codity review

@codity-chait

codity-chait Bot commented Jun 8, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added automatic backend upgrade detection with optional auto-upgrades, running every 6 hours with distributed coordination via PostgreSQL advisory locks.
  • New CLI commands and HTTP endpoints to check, trigger, and apply backend upgrades with atomic replacement and rollback on failure.
  • UI now shows available upgrades, version info, and an "Auto-upgrade Backends" setting.

Key Changes by Area

Backend Management: Added UpgradeBackend() and CheckUpgrades() to BackendManager interface with implementations for local and distributed modes. Uses OCI image digests for cheap version comparison without full downloads.

Configuration: New AutoUpgradeBackends flag (env: AUTO_UPGRADE_BACKENDS, default false) with RuntimeSettings integration for live updates.

HTTP API: New endpoints at /backends/upgrades (GET cached, POST trigger check) and POST /backends/upgrade/:name for specific backend upgrades.

UI: Upgrade banners, version badges, toggle filters for "Show all" and "Development" backends, and settings toggle for auto-upgrades.

Distributed Coordination: UpgradeChecker service uses advisory locks to prevent duplicate checks across replicas; DistributedBackendManager fans out upgrades via NATS.

Files Changed

File Changes Summary
core/application/application.go Wired UpgradeChecker into application lifecycle
core/application/config_file_watcher.go Added AutoUpgradeBackends to runtime settings (see review finding)
core/application/startup.go Starts UpgradeChecker with 30s delay when galleries configured
core/application/upgrade_checker.go New background service for periodic upgrade checks with distributed locking
core/cli/backends.go Added backends upgrade command and enhanced backends list
core/cli/run.go Added AutoUpgradeBackends CLI flag
core/config/application_config.go Added AutoUpgradeBackends configuration field
core/config/application_config_test.go Tests for new configuration
core/config/runtime_settings.go Added AutoUpgradeBackends runtime setting
core/gallery/backend_types.go Added Version, URI, Digest fields to metadata structs
core/gallery/backends.go Version tracking during installation
core/gallery/backends_version_test.go Tests for version detection
core/gallery/upgrade.go New upgrade detection and atomic upgrade with rollback
core/gallery/upgrade_test.go Tests for upgrade flow and failure recovery
core/http/endpoints/localai/backend.go UpgradeInfoProvider interface and endpoint wiring
core/http/react-ui/src/pages/Backends.jsx Upgrade banner, filters, individual upgrade handling
core/http/react-ui/src/pages/Manage.jsx Version display and upgrade/reinstall buttons
core/http/react-ui/src/pages/Settings.jsx Auto-upgrade backends toggle
core/http/react-ui/src/utils/api.js API client methods for upgrades
core/http/react-ui/src/utils/config.js Config utilities for upgrade settings
core/http/routes/localai.go New upgrade API routes
core/http/routes/ui_api.go UI API route registration
core/http/routes/ui_api_backends_test.go Tests for upgrade endpoints
core/services/advisorylock/keys.go New KeyBackendUpgradeCheck advisory lock key
core/services/galleryop/backends.go Added Upgrade flag to ManagementOp
core/services/galleryop/managers.go BackendManager interface with upgrade methods
core/services/galleryop/managers_local.go Local upgrade implementation
core/services/galleryop/operation.go Operation types for upgrades
core/services/nodes/managers_distributed.go Distributed upgrade via NATS fanout
pkg/oci/image.go GetImageDigest() for remote digest fetching
pkg/oci/image_test.go Tests for digest fetching

Review Focus Areas

  • Race condition in upgrade checker: upgrade_checker.go:77 runs initial check before advisory lock guard, causing duplicate upgrades when AutoUpgradeBackends=true with multiple replicas.
  • Environment precedence bug: config_file_watcher.go:338-340 applies AutoUpgradeBackends from runtime settings without checking envAutoUpgradeBackends guard, violating env var precedence.

Architecture

  • Design Decisions: OCI digests enable cheap version comparison without downloading images. Atomic upgrades use temp directories with backup rollback. Distributed mode reuses install endpoint with force flag (noted as TODO for dedicated NATS subject).
  • Scalability & Extensibility: Advisory locks prevent duplicate work in clustered deployments. Gallery comparison remains global, so CheckUpgrades() delegates to local manager in distributed mode.
  • Risks: Initial unconditional check (upgrade_checker.go:77) is an unintentional race. Env var precedence violation is unintentional. Both flagged in review findings.

Comment on lines +76 to +77
// First check always runs locally (to warm the cache on this instance)
uc.runCheck(ctx)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Critical

The initial uc.runCheck(ctx) call at line 77 executes unconditionally on every frontend instance before the distributed advisory-lock guard is installed. When AutoUpgradeBackends = true and multiple frontend replicas start simultaneously, each replica calls gallery.UpgradeBackend concurrently for the same backends, racing on filesystem paths like .upgrade-tmp and .backup directories, causing corrupted binaries and undefined ordering of atomic rename operations.

Suggested fix
	// First check always runs locally (to warm the cache on this instance)
		if uc.db == nil {
			// Only run initial check in standalone mode
			// In distributed mode, wait for advisory lock to avoid concurrent upgrade races
			uc.runCheck(ctx)
		}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/upgrade_checker.go
Lines: 76-77
Issue Type: robustness-critical
Severity: critical

Issue Description:
The initial `uc.runCheck(ctx)` call at line 77 executes unconditionally on every frontend instance before the distributed advisory-lock guard is installed. When `AutoUpgradeBackends = true` and multiple frontend replicas start simultaneously, each replica calls `gallery.UpgradeBackend` concurrently for the same backends, racing on filesystem paths like `.upgrade-tmp` and `.backup` directories, causing corrupted binaries and undefined ordering of atomic rename operations.

Current Code:
	// First check always runs locally (to warm the cache on this instance)
	uc.runCheck(ctx)

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +338 to +340
if settings.AutoUpgradeBackends != nil {
appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness High

The AutoUpgradeBackends setting from runtime_settings.json is applied without checking the envAutoUpgradeBackends guard that all other similarly-gated settings use in the same function. This causes environment variable precedence to be violated: if LOCALAI_AUTO_UPGRADE_BACKENDS=false is explicitly set as an environment variable, a runtime_settings.json file containing {"auto_upgrade_backends": true} will silently override this policy.

Suggested fix
			if settings.AutoUpgradeBackends != nil && !envAutoUpgradeBackends {
							appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends
						}
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: core/application/config_file_watcher.go
Lines: 338-340
Issue Type: robustness-high
Severity: high

Issue Description:
The `AutoUpgradeBackends` setting from `runtime_settings.json` is applied without checking the `envAutoUpgradeBackends` guard that all other similarly-gated settings use in the same function. This causes environment variable precedence to be violated: if `LOCALAI_AUTO_UPGRADE_BACKENDS=false` is explicitly set as an environment variable, a `runtime_settings.json` file containing `{"auto_upgrade_backends": true}` will silently override this policy.

Current Code:
			if settings.AutoUpgradeBackends != nil {
				appConfig.AutoUpgradeBackends = *settings.AutoUpgradeBackends
			}

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@codity-chait

codity-chait Bot commented Jun 8, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 758
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 6
Low Risk (Permissive) 659
Unknown License 93

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 6 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.2

MPL-2.0 (5 packages):

  • github.com/philippgille/chromem-go 0.7.0
  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 93 packages
  • github.com/dslipak/pdf 0.0.2
  • github.com/eritikass/githubmarkdownconvertergo 0.1.10
  • github.com/go-git/go-billy/v5 5.6.2
  • github.com/go-git/gcfg 1.5.1-0.20230307220236-3a3c6141e376
  • github.com/go-git/go-git/v5 5.16.4
  • github.com/gocolly/colly 1.2.0
  • github.com/golang/protobuf 1.5.4
  • github.com/google/go-github/v69 69.2.0
  • github.com/google/go-querystring 1.1.0
  • github.com/kennygrant/sanitize 1.2.4
  • github.com/mschoch/smat 0.2.0
  • github.com/olekukonko/tablewriter 0.0.5
  • github.com/mudler/skillserver 0.0.6
  • github.com/pjbgf/sha1cd 0.3.2
  • github.com/segmentio/asm 1.1.3
  • github.com/saintfish/chardet 0.0.0-20230101081208-5e3ef4b5456d
  • github.com/skeema/knownhosts 1.3.1
  • go.mau.fi/util 0.3.0
  • go.starlark.net 0.0.0-20250417143717-f57e51f710eb
  • gopkg.in/warnings.v0 0.1.2

...and 73 more

Powered by Codity.ai · Docs

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.

3 participants