Skip to content

release: v0.3.0-beta.3 - #168

Merged
gnanirahulnutakki merged 11 commits into
mainfrom
dev
Jul 15, 2026
Merged

release: v0.3.0-beta.3#168
gnanirahulnutakki merged 11 commits into
mainfrom
dev

Conversation

@gnanirahulnutakki

@gnanirahulnutakki gnanirahulnutakki commented Jul 15, 2026

Copy link
Copy Markdown
Member

Native fleet IDE beta\n\nPromotes the fully verified F11.7 kubeconfig-directory importer and F11.8 native macOS fleet desktop from dev to main for the immutable v0.3.0-beta.3 release tag.\n\n### What beta.3 delivers\n\n- signed/provenance-backed darwin/arm64 CLI archive, including sith ui and the native sith desktop command\n- privacy-first local kubeconfig-folder import through the native picker; no TCP listener, account, telemetry, or kubeconfig-path leak\n- all previous release archive, SPDX SBOM, Sigstore, SLSA, and Homebrew-formula protections\n\n### Deliberate distribution boundary\n\nThis is a beta CLI archive, not a separately distributable notarized macOS .app. sith desktop is runnable from the macOS ARM64 archive, while Developer ID signing, notarization, and stapling for a Finder-distributed .app remain an E9 follow-up.\n\n### Evidence\n\n- exact dev merge CI for 7f9302d: run 29381529839 passed build, race tests, tenant isolation, reproducible archives/SBOM, and real two-cluster fan-out\n- PR #167 received red-team review corrections before merge; its exact post-merge CI passed\n- v0.3.0-beta.2 is immutable and remains untouched\n\nAfter this release PR is green and merged, the exact main CI will be verified before the SSH-signed v0.3.0-beta.3 tag is created.

Summary by CodeRabbit

  • New Features

    • Added bounded kubeconfig directory importing for the web UI, including source labels and safe diagnostics.
    • Added a native macOS desktop app experience with an in-app “Import folder” chooser.
    • Added the desktop command and ARM64 macOS app build support.
    • Preserved existing browser-based UI access alongside the desktop experience.
  • Bug Fixes

    • Improved validation and privacy protections for invalid paths, symlinks, import failures, and diagnostic messages.
  • Documentation

    • Added build instructions and architecture documentation for directory imports and the native desktop app.

Gnani Rahul and others added 11 commits July 14, 2026 17:32
Add a bounded, in-memory kubeconfig directory importer for the loopback fleet IDE. Imported contexts retain unique identities, safe relative source labels, and per-file diagnostics while preserving the direct local-operation path.

GSTACK-Checkpoint: 2026-07-14/f11-kubeconfig-directory-import#1
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
GSTACK-Checkpoint: 2026-07-14/f11-kubeconfig-directory-import#2
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
…onfig-directory-import

feat(f11): import kubeconfig directories in local UI
Cap all filesystem traversal entries, redact root traversal errors, and prove multi-context source filtering in the real cluster suite.

GSTACK-Checkpoint: 2026-07-14/f11-directory-import-review#1
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
…ory-import-review

fix(f11): harden kubeconfig directory import review gaps
Keep the all-entry traversal cap as the sole directory-import limit after peer review.

GSTACK-Checkpoint: 2026-07-14/f11-directory-entry-cap#1
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
…ory-entry-cap

fix(f11): remove redundant directory candidate bound
Package the existing local fleet IDE in a Wails v2 macOS shell with a native, path-private kubeconfig-folder import flow. Preserve the hardened in-process UI boundary and atomic session handoff.

GSTACK-Checkpoint: 2026-07-14/f11-native-desktop-shell#1
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
Use non-blocking session leases, a sanitized hydration-stop state, directory-only startup, graceful signal shutdown, and reproducible pinned Wails packaging with ARM64 verification.

GSTACK-Checkpoint: 2026-07-14/f11-native-desktop-shell#2
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
Signed-off-by: Gnani Rahul <gnani.nutakki@gmail.com>
…e-desktop-shell

feat(f11): add native macOS fleet desktop
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ba70fd29-a520-4fc5-bea6-f6b2f87cde58

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds bounded kubeconfig directory import with safe diagnostics and source metadata, integrates it into the WebUI, and introduces a Wails-based macOS desktop shell with native folder selection, in-process request handling, ARM64 packaging, signing checks, documentation, and tests.

Changes

Kubeconfig directory import and metadata

Layer / File(s) Summary
Bounded directory import and metadata
internal/connector/..., internal/fleetcache/..., internal/cli/ui.go, tests/e2e/...
Directory imports enforce filesystem, depth, count, and size limits; merge contexts with source metadata; expose sanitized diagnostics through discovery and snapshots; and are covered by unit and end-to-end tests.

In-process WebUI

Layer / File(s) Summary
In-process WebView routing and safe UI metadata
internal/webui/..., internal/webui/assets/..., internal/privacy/...
Adds Wails-origin validation, request-draining handler replacement, safe source/diagnostic rendering, and an opt-in folder-import control.

Desktop runtime

Layer / File(s) Summary
Desktop command and native bridge
internal/cli/desktop*.go, internal/cli/root.go, internal/cli/desktop_test.go
Adds the macOS desktop command, hydrated session lifecycle, native directory chooser, atomic session replacement, cancellation shutdown, and non-Darwin behavior.

Packaging and documentation

Layer / File(s) Summary
macOS packaging and project documentation
cmd/sith-desktop/..., Makefile, go.mod, README.md, docs/adr/..., sessions/...
Adds Wails configuration and dependencies, ARM64 app build/signing checks, desktop documentation, ADR material, and session records.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • ArdurAI/sith#163 — Provides the shared kubeconfig-directory import wiring and discovery, cache, and WebUI metadata behavior used by this desktop integration.
  • ArdurAI/sith#164 — Further hardens the directory importer and its traversal and error-handling tests.
  • ArdurAI/sith#165 — Relates to directory-import entry limits and the corresponding limit diagnostic.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s purpose as a release promotion for v0.3.0-beta.3 and is concise and specific enough for history scanning.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands.

@gnanirahulnutakki

Copy link
Copy Markdown
Member Author

@coderabbitai review

Please review this dev-to-main beta promotion for release-boundary regressions only: tag ancestry, beta/latest semantics, macOS ARM64 archive claims, release provenance, and privacy wording. The code changes were individually reviewed and CI-verified in their feature PRs.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
gh pr view 168 --repo ArdurAI/sith --json title,body,baseRefName,headRefName,commits,files,mergeable,additions,deletions
echo "---DIFF STAT---"
gh pr diff 168 --repo ArdurAI/sith --stat
echo "---FILES---"
gh pr diff 168 --repo ArdurAI/sith --name-only

Length of output: 11634


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd /tmp 2>/dev/null || true
echo "--- README diff ---"
gh pr diff 168 --repo ArdurAI/sith --patch -- README.md || true
echo "--- Makefile diff ---"
gh pr diff 168 --repo ArdurAI/sith --patch -- Makefile || true
echo "--- tags ---"
gh api repos/ArdurAI/sith/tags --jq '.[].name' | head -50
echo "--- releases ---"
gh api repos/ArdurAI/sith/releases --jq '.[] | {tag: .tag_name, prerelease, draft, created_at}' | head -50
echo "--- workflow files ---"
gh api repos/ArdurAI/sith/contents/.github/workflows --jq '.[].name' 2>/dev/null || true

Length of output: 1299


🏁 Script executed:

#!/bin/bash
set -euo pipefail
git clone --depth 100 https://github.com/ArdurAI/sith.git repo 2>&1 | tail -5
cd repo
git fetch origin dev main 2>&1 | tail -5
echo "--- merge-base ---"
git merge-base main origin/dev
git log --oneline main..origin/dev | tail -30
echo "--- README diff ---"
git diff main origin/dev -- README.md
echo "--- Makefile diff ---"
git diff main origin/dev -- Makefile
echo "--- release.yml diff ---"
git diff main origin/dev -- .github/workflows/release.yml

Length of output: 1065


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd repo
git remote set-branches origin '*'
git fetch --depth 200 origin dev main
echo "--- merge-base ---"
git merge-base origin/main origin/dev
echo "--- commits main..dev ---"
git log --oneline origin/main..origin/dev
echo "--- README diff ---"
git diff origin/main origin/dev -- README.md
echo "--- Makefile diff ---"
git diff origin/main origin/dev -- Makefile
echo "--- release.yml diff ---"
git diff origin/main origin/dev -- .github/workflows/release.yml

Length of output: 7462


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd repo
echo "--- tag beta.2 commit ---"
git rev-list -n1 v0.3.0-beta.2
echo "--- is beta.2 ancestor of dev head 7f9302d ---"
git merge-base --is-ancestor v0.3.0-beta.2 origin/dev && echo yes || echo no
echo "--- is beta.2 ancestor of main ---"
git merge-base --is-ancestor v0.3.0-beta.2 origin/main && echo yes || echo no
echo "--- current main head ---"
git rev-parse origin/main
echo "--- current dev head ---"
git rev-parse origin/dev
echo "--- check CI run status for 29381529839 ---"
gh run view 29381529839 --repo ArdurAI/sith --json status,conclusion,headBranch,headSha,workflowName 2>&1 || true
echo "--- check PR 167 status ---"
gh pr view 167 --repo ArdurAI/sith --json state,mergedAt,mergeCommit 2>&1 || true

Length of output: 1506


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd repo
echo "--- commits in main not in dev ---"
git log --oneline origin/dev..origin/main
echo "--- commits count ---"
git log --oneline origin/dev..origin/main | wc -l

Length of output: 692


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd repo
fd -i goreleaser
echo "--- goreleaser diff dev vs main ---"
git diff origin/main origin/dev -- .goreleaser.yml .goreleaser.yaml 2>/dev/null || true
echo "--- goreleaser darwin/arm64 mention ---"
rg -n 'darwin|arm64' .goreleaser.yml 2>/dev/null | head -20
echo "--- homebrew formula ---"
fd -i formula
rg -n 'homebrew|formula' .goreleaser.yml 2>/dev/null
echo "--- SLSA/SBOM/sigstore refs in release.yml ---"
rg -n 'slsa|sbom|sigstore|cosign|provenance' .github/workflows/release.yml -i

Length of output: 907


🏁 Script executed:

#!/bin/bash
cd repo
echo "--- find goreleaser config ---"
fd -i -a goreleaser 2>/dev/null
find . -iname '*goreleaser*' -not -path '*/.git/*'
echo "--- release.yml content search ---"
grep -inE 'slsa|sbom|sigstore|cosign|provenance|darwin|arm64|homebrew|formula' .github/workflows/release.yml

Length of output: 3467


This confirms release.yml provides the darwin/arm64 SBOM/provenance/Sigstore/Homebrew claims made in the PR body, and this workflow is unchanged between dev and main in this PR, so the release-provenance mechanism carries over intact as claimed.

Release-boundary review summary

@gnanirahulnutakki I focused strictly on the boundary items you asked about — no code-logic re-review, since that already happened in #163#167.

Tag ancestry & immutability

  • v0.3.0-beta.2 still points at fa16695 (current main HEAD) — untouched, as claimed.
  • main..dev contains exactly the 11 commits from PR #163#167 (F11.7 kubeconfig-directory importer + F11.8 native desktop), landing at 7f9302d, matching the PR body's "exact dev merge CI for 7f9302d."
  • dev..main shows 6 commits, but these are prior "Merge pull request #NNN from ArdurAI/dev" merge commits on main from earlier promotions (#110, #114, #142, #145, #156, #161) — not literal ancestors of dev history, but their content is already folded into dev's current tree via the normal repeated promotion cadence. The PR diff (main...dev) contains only new F11-related additions, no unexplained deletions/reversions, confirming tree parity — this is not a regression.
  • CI run 29381529839 on dev@7f9302d: confirmed status: completed, conclusion: success.
  • PR #167 confirmed merged into dev at 7f9302d.

beta/latest semantics

  • GitHub Releases: v0.3.0-beta.1 (draft, prerelease), v0.3.0-beta.2 (prerelease, not draft), v0.2.1/v0.1.0 (stable, not prerelease). Beta tags correctly flagged as prereleases, so beta.3 promotion won't misfire "latest" semantics for stable-release consumers.
  • No v0.3.0-beta.3 tag exists yet — consistent with the PR body's stated plan to tag only after this PR is green and merged.

macOS ARM64 archive claims

  • .github/workflows/release.yml is byte-identical between main and dev (no diff) — the existing darwin/amd64 + darwin/arm64 SBOM attestation, SLSA provenance, Cosign signing, and Homebrew formula signing steps carry over unchanged, matching "all previous release archive, SPDX SBOM, Sigstore, SLSA, and Homebrew-formula protections."
  • New Makefile target desktop-build explicitly restricts itself to darwin/arm64 (wails build ... -platform darwin/arm64, lipo -archs asserts arm64), and is ad-hoc-signed only (codesign --sign -), matching the README's stated "not yet a distributed replacement" / "public releases remain blocked on Developer ID signing, notarization, stapling" boundary language. No claim of notarized .app distribution appears anywhere in the diff.

Release provenance

  • No changes to .github/workflows/release.yml, .goreleaser.yaml, or signing/attestation logic in this PR — release provenance mechanics are unmodified, so the "beta" promotion introduces no new provenance surface beyond the CLI archive already covered by the existing pipeline.

Privacy wording

  • README additions for --kubeconfig-dir and sith desktop explicitly state: no persisted config, no symlink-following, bounded traversal (128 entries / 4 MiB per file / 8 directory levels), relative-filename-only labeling, and sanitized failure reporting ("never the selected absolute path or kubeconfig content").
  • Desktop section explicitly states wails://wails in-process origin, "does not open a TCP listener" — matches the PR body's "no TCP listener, account, telemetry, or kubeconfig-path leak" claim, and is consistent with the existing loopback-only privacy boundary language elsewhere in the file.

No release-boundary regressions found. I did not re-review the F11.7/F11.8 implementation logic itself, per your scope request.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
internal/fleetcache/store_test.go (1)

445-456: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Verify diagnostics slice isolation.

Mutate the original diagnostics after SetDiscovery, then mutate the first snapshot and query again. The current assertions would still pass if either clone were accidentally removed.

Proposed test extension
-	store.SetDiscovery(fleet.LocalWorkspace, connector.Discovery{
+	diagnostics := []connector.Diagnostic{{Source: "bad.yaml", Message: "invalid kubeconfig"}}
+	store.SetDiscovery(fleet.LocalWorkspace, connector.Discovery{
 		Scopes: []connector.Scope{{
 			Name: "import-123/context/prod", DisplayName: "prod", Origin: "team-a.yaml", Reachable: true, ObservedAt: now,
 		}},
-		Diagnostics: []connector.Diagnostic{{Source: "bad.yaml", Message: "invalid kubeconfig"}},
+		Diagnostics: diagnostics,
 	})
+	diagnostics[0].Message = "mutated input"
 	snapshot := store.Query(fleet.LocalWorkspace, Query{})
...
+	snapshot.Diagnostics[0].Message = "mutated output"
+	if got := store.Query(fleet.LocalWorkspace, Query{}).Diagnostics[0].Message; got != "invalid kubeconfig" {
+		t.Fatalf("stored diagnostic was mutated through snapshot: %q", got)
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/fleetcache/store_test.go` around lines 445 - 456, Extend the
SetDiscovery/Query test around store.SetDiscovery to verify diagnostics slice
isolation: mutate the original diagnostics after storing, mutate the diagnostics
in the first snapshot, then query again and assert the stored result remains
unchanged with the expected diagnostic. Keep the existing scope assertions
intact.
internal/webui/server_test.go (1)

177-191: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Exercise redaction with an actual local path.

The fixture contains only team-a.yaml and broken.yaml, so the /Users/ assertion passes even if absolute Origin or diagnostic-source values are serialized unchanged. Seed a representative absolute path at the layer responsible for redaction and assert that only the safe filename reaches the snapshot.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/webui/server_test.go` around lines 177 - 191, Update the snapshot
fixture setup in the test around SetDiscovery to seed an actual absolute local
path in the Origin or diagnostic source field handled by redaction, while
retaining the existing fixture coverage. Extend the response assertion to
require the safe filename and reject the absolute path prefix, verifying that
the redaction layer—not incidental fixture names—produces the sanitized
snapshot.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cli/desktop_execute.go`:
- Around line 17-22: Update the error handling around runDesktop to recognize
expected context cancellation from the signal.NotifyContext shutdown path and
return a successful exit code instead of 1; preserve stderr reporting and the
existing failure exit code for other errors.

In `@internal/cli/ui.go`:
- Around line 43-49: Reorder the initialization in the surrounding UI setup so
the options.kubeconfigDir branch constructs and assigns the directory adapter
before the existing nil-client validation runs. Then validate the resulting
reader and local dependencies, preserving the current kubeconfig.New error
wrapping and normal client initialization behavior.

In `@internal/connector/kubeconfig/directory.go`:
- Around line 145-150: Update the file-opening logic in the kubeconfig import
flow around the os.Open call to prevent symlink traversal for the final path
component after WalkDir classification. Open the file relative to a handle
pinned to the selected directory, or use an equivalent no-follow strategy, while
preserving the existing size limit, error propagation, and deferred close
behavior.

---

Nitpick comments:
In `@internal/fleetcache/store_test.go`:
- Around line 445-456: Extend the SetDiscovery/Query test around
store.SetDiscovery to verify diagnostics slice isolation: mutate the original
diagnostics after storing, mutate the diagnostics in the first snapshot, then
query again and assert the stored result remains unchanged with the expected
diagnostic. Keep the existing scope assertions intact.

In `@internal/webui/server_test.go`:
- Around line 177-191: Update the snapshot fixture setup in the test around
SetDiscovery to seed an actual absolute local path in the Origin or diagnostic
source field handled by redaction, while retaining the existing fixture
coverage. Extend the response assertion to require the safe filename and reject
the absolute path prefix, verifying that the redaction layer—not incidental
fixture names—produces the sanitized snapshot.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f7bec039-b3c3-4d2d-bd9e-b50d00804ebc

📥 Commits

Reviewing files that changed from the base of the PR and between fa16695 and 7f9302d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (38)
  • .gitignore
  • Makefile
  • README.md
  • cmd/sith-desktop/frontend/.gitkeep
  • cmd/sith-desktop/main.go
  • cmd/sith-desktop/wails.json
  • docs/adr/0010-native-local-desktop-shell.md
  • docs/adr/README.md
  • go.mod
  • internal/cli/desktop.go
  • internal/cli/desktop_darwin.go
  • internal/cli/desktop_execute.go
  • internal/cli/desktop_other.go
  • internal/cli/desktop_test.go
  • internal/cli/local_test.go
  • internal/cli/root.go
  • internal/cli/ui.go
  • internal/connector/contract.go
  • internal/connector/kubeconfig/adapter.go
  • internal/connector/kubeconfig/directory.go
  • internal/connector/kubeconfig/directory_test.go
  • internal/connector/kubeconfig/local_objects.go
  • internal/fleetcache/store.go
  • internal/fleetcache/store_test.go
  • internal/privacy/boundary_test.go
  • internal/webui/assets/app.css
  • internal/webui/assets/app.js
  • internal/webui/assets/index.html
  • internal/webui/desktop.go
  • internal/webui/desktop_test.go
  • internal/webui/server.go
  • internal/webui/server_test.go
  • sessions/2026-07-14-f11-directory-entry-cap-cleanup.md
  • sessions/2026-07-14-f11-directory-import-review-fixes.md
  • sessions/2026-07-14-f11-kubeconfig-directory-import.md
  • sessions/2026-07-14-f11-native-desktop-shell.md
  • tests/e2e/kind_fanout_test.go
  • tests/e2e/kind_web_ui_test.go

Comment on lines +17 to +22
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
if err := runDesktop(ctx, adapter, adapter, ""); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
return 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== files ==\n'
git ls-files 'internal/cli/desktop_execute.go' 'cmd/sith-desktop/main.go' 'internal/cli/*' | sed 's#^`#-` #'

printf '\n== desktop entrypoint references ==\n'
rg -n "runDesktop|NotifyContext|SIGTERM|os.Interrupt|sith-desktop|desktop_execute" internal cmd -g '!**/node_modules/**' || true

printf '\n== file excerpts ==\n'
for f in internal/cli/desktop_execute.go cmd/sith-desktop/main.go; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    nl -ba "$f" | sed -n '1,120p'
  fi
done

Repository: ArdurAI/sith

Length of output: 1894


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for p in [Path('internal/cli/desktop_execute.go'), Path('cmd/sith-desktop/main.go')]:
    print(f'## {p}')
    if p.exists():
        for i, line in enumerate(p.read_text().splitlines(), 1):
            print(f'{i:4d}: {line}')
PY

Repository: ArdurAI/sith

Length of output: 1172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in internal/cli/desktop_darwin.go internal/cli/desktop_other.go internal/cli/desktop.go internal/cli/root.go; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    python3 - <<'PY' "$f"
from pathlib import Path
import sys
p = Path(sys.argv[1])
for i, line in enumerate(p.read_text().splitlines(), 1):
    print(f"{i:4d}: {line}")
PY
    echo
  fi
done

echo "--- build tags / signals ---"
rg -n "^//go:build|NotifyContext|os\.Interrupt|SIGTERM|context\.Canceled|Canceled" internal/cli cmd -g '!**/node_modules/**'

Repository: ArdurAI/sith

Length of output: 19162


🏁 Script executed:

#!/bin/bash
set -euo pipefail
for f in internal/cli/desktop_darwin.go internal/cli/desktop_other.go internal/cli/desktop.go internal/cli/root.go; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    python3 - <<'PY' "$f"
from pathlib import Path
import sys
p = Path(sys.argv[1])
for i, line in enumerate(p.read_text().splitlines(), 1):
    print(f"{i:4d}: {line}")
PY
    echo
  fi
done

echo "--- build tags / signals ---"
rg -n "^//go:build|NotifyContext|os\.Interrupt|SIGTERM|context\.Canceled|Canceled" internal/cli cmd -g '!**/node_modules/**'

Repository: ArdurAI/sith

Length of output: 19162


🌐 Web query:

Wails v2 quit shutdown wails.Run returns error context canceled signal.NotifyContext os.Interrupt SIGTERM

💡 Result:

In Wails v2, the observation that wails.Run may return a context canceled error after receiving an OS interrupt (such as Ctrl+C) is related to how the framework handles process termination and signal monitoring [1][2]. Wails v2 maintains an internal signal manager that listens for OS signals like SIGINT (Interrupt) and SIGTERM [1]. When these signals are detected, the framework triggers a shutdown process [1]. If your application or a parent process uses signal.NotifyContext to manage application lifecycle, the cancellation of that context is often the expected and correct behavior when the application is requested to shut down via these signals [3]. Key points regarding this behavior: 1. Signal Handling Integration: Wails v2 hooks into OS signals to initiate a graceful shutdown, which includes running defined OnShutdown callbacks [1][4]. 2. Context Cancellation: The error context canceled is a standard Go result when a context derived from signal.NotifyContext is cancelled by a caught OS signal [3]. If your application logic relies on this context, it will naturally receive this signal as a trigger to stop operations [5]. 3. Premature Termination Issues: Historically, some users have reported that terminating via Ctrl+C can cause an immediate exit that may bypass or truncate custom cleanup routines [2]. If your shutdown logic is not completing, it is generally recommended to ensure that critical cleanup tasks are handled within the OnShutdown lifecycle hook provided in the application options [4][6], rather than relying on the main thread to block indefinitely after receiving a termination signal. If you are seeing this error during development or execution and it is causing unexpected crashes, ensure that your application is not prematurely closing resources that the Wails runtime still requires during its own internal shutdown phase [2]. If the application terminates cleanly despite the error, it is typically an indicator that the context cancellation was successful [3].

Citations:


Preserve graceful shutdown semantics. Treat expected cancellation from the desktop launcher as a clean exit instead of returning exit code 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/desktop_execute.go` around lines 17 - 22, Update the error
handling around runDesktop to recognize expected context cancellation from the
signal.NotifyContext shutdown path and return a successful exit code instead of
1; preserve stderr reporting and the existing failure exit code for other
errors.

Comment thread internal/cli/ui.go
Comment on lines +43 to +49
if options.kubeconfigDir != "" {
adapter, err := kubeconfig.New(kubeconfig.WithDirectory(options.kubeconfigDir))
if err != nil {
return fmt.Errorf("import kubeconfig directory: %w", err)
}
reader, local = adapter, adapter
}

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 Correctness | 🟠 Major | ⚡ Quick win

Construct the directory adapter before rejecting missing clients.

The preceding nil check runs first, so directory-only startup fails before this branch can provide both clients. Select the adapter first, then validate the resulting dependencies.

Proposed fix
 		RunE: func(command *cobra.Command, _ []string) error {
-			if reader == nil || local == nil {
-				return fmt.Errorf("local fleet UI requires a Kubernetes reader and local operations client")
-			}
 			if options.kubeconfigDir != "" {
 				adapter, err := kubeconfig.New(kubeconfig.WithDirectory(options.kubeconfigDir))
 				if err != nil {
 					return fmt.Errorf("import kubeconfig directory: %w", err)
 				}
 				reader, local = adapter, adapter
 			}
+			if reader == nil || local == nil {
+				return fmt.Errorf("local fleet UI requires a Kubernetes reader and local operations client")
+			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if options.kubeconfigDir != "" {
adapter, err := kubeconfig.New(kubeconfig.WithDirectory(options.kubeconfigDir))
if err != nil {
return fmt.Errorf("import kubeconfig directory: %w", err)
}
reader, local = adapter, adapter
}
if options.kubeconfigDir != "" {
adapter, err := kubeconfig.New(kubeconfig.WithDirectory(options.kubeconfigDir))
if err != nil {
return fmt.Errorf("import kubeconfig directory: %w", err)
}
reader, local = adapter, adapter
}
if reader == nil || local == nil {
return fmt.Errorf("local fleet UI requires a Kubernetes reader and local operations client")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/ui.go` around lines 43 - 49, Reorder the initialization in the
surrounding UI setup so the options.kubeconfigDir branch constructs and assigns
the directory adapter before the existing nil-client validation runs. Then
validate the resulting reader and local dependencies, preserving the current
kubeconfig.New error wrapping and normal client initialization behavior.

Comment on lines +145 to +150
file, err := os.Open(path) // #nosec G304 -- path is discovered beneath a user-selected directory without symlink traversal.
if err != nil {
return nil, err
}
defer func() { _ = file.Close() }()
payload, err := io.ReadAll(io.LimitReader(file, maxImportBytes+1))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the repository already provides a no-follow/openat helper.
rg -n --type=go -C3 '\b(Openat|O_NOFOLLOW|open.*NoFollow|WalkDir)\b' .

Repository: ArdurAI/sith

Length of output: 2537


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the kubeconfig directory loader around the reported lines.
file="internal/connector/kubeconfig/directory.go"
wc -l "$file"
sed -n '1,240p' "$file"

Repository: ArdurAI/sith

Length of output: 7508


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any no-follow/openat helper or tests around symlink replacement in kubeconfig loading.
rg -n --type=go -C3 '\b(Openat|O_NOFOLLOW|no-?follow|symlink|LocationOfOrigin|ResolveLocalPaths|loadKubeconfigFile)\b' internal/connector/kubeconfig internal/privacy

Repository: ArdurAI/sith

Length of output: 4232


Pin file opens to the selected directory.

A file can still be swapped in after WalkDir classifies it, so os.Open(path) may follow a symlink and read content outside the chosen directory. Open through a pinned directory handle or use a no-follow strategy for the final path component.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/connector/kubeconfig/directory.go` around lines 145 - 150, Update
the file-opening logic in the kubeconfig import flow around the os.Open call to
prevent symlink traversal for the final path component after WalkDir
classification. Open the file relative to a handle pinned to the selected
directory, or use an equivalent no-follow strategy, while preserving the
existing size limit, error propagation, and deferred close behavior.

@gnanirahulnutakki
gnanirahulnutakki merged commit 0cc43aa into main Jul 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant