Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ coverage.*
node_modules/
dist/
build/
cmd/sith-desktop/frontend/wailsjs/
.next/
coverage/
20 changes: 19 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ GOVULNCHECK ?= govulncheck
KIND ?= kind
HELM ?= helm
GORELEASER ?= goreleaser
WAILS ?= wails
WAILS_VERSION ?= v2.12.0
CODESIGN ?= codesign
PLISTBUDDY ?= /usr/libexec/PlistBuddy
LIPO ?= lipo
DOCKER ?= docker
KUBECTL ?= kubectl
OCM_SCRATCH_ROOT ?= $(shell python3 -c 'import os; print(os.path.join(os.path.realpath(os.environ.get("TMPDIR", "/tmp")), "sith-m0-{}".format(os.getuid()), "lab"))')
Expand All @@ -29,14 +34,27 @@ LDFLAGS := -s -w \
-X $(PKG)/internal/buildinfo.Commit=$(COMMIT) \
-X $(PKG)/internal/buildinfo.Date=$(DATE)

.PHONY: all build test test-scripts perf e2e e2e-helm e2e-oci e2e-kind e2e-ocm e2e-postgres e2e-isolation lint vuln fmt fmt-check vet tidy clean run ci release-check help
.PHONY: all build desktop-build test test-scripts perf e2e e2e-helm e2e-oci e2e-kind e2e-ocm e2e-postgres e2e-isolation lint vuln fmt fmt-check vet tidy clean run ci release-check help

all: build

build: ## Build the sith binary into bin/
@mkdir -p $(BIN_DIR)
go build -trimpath -ldflags '$(LDFLAGS)' -o $(BIN_DIR)/$(BINARY) $(CMD)

desktop-build: ## Build the ad-hoc-signed macOS arm64 Sith.app development bundle
@command -v "$(WAILS)" >/dev/null || { echo "Wails $(WAILS_VERSION) is required" >&2; exit 1; }
@"$(WAILS)" version | grep -q '$(WAILS_VERSION)' || { echo "Wails $(WAILS_VERSION) is required" >&2; exit 1; }
cd cmd/sith-desktop && "$(WAILS)" build -clean -m -nosyncgomod -s -trimpath -platform darwin/arm64
@set -euo pipefail; \
app='cmd/sith-desktop/build/bin/Sith.app'; \
test -d "$$app"; \
"$(LIPO)" -archs "$$app/Contents/MacOS/Sith" | grep -qx 'arm64'; \
"$(PLISTBUDDY)" -c 'Set :CFBundleIdentifier com.ardurai.sith' "$$app/Contents/Info.plist"; \
"$(CODESIGN)" --force --sign - "$$app"; \
"$(CODESIGN)" --verify --strict "$$app"; \
plutil -extract CFBundleIdentifier raw -o - "$$app/Contents/Info.plist" | grep -qx 'com.ardurai.sith'

test: ## Run unit tests with the race detector and report coverage
go test -race -count=1 -coverprofile=coverage.out ./...

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ make build
./bin/sith edit configmap/api-settings --context kind-dev -n apps
./bin/sith ui # loopback-only embedded fleet IDE
./bin/sith ui --kubeconfig-dir "$HOME/kubeconfigs" # import a folder of kubeconfig files for this UI session
./bin/sith desktop # native macOS window for the same local fleet IDE
./bin/sith serve --mcp # loopback-only MCP read server
./bin/sith serve --mcp --require-token
```
Expand Down Expand Up @@ -360,6 +361,16 @@ diff. Port-forward accepts loopback addresses only (`localhost`, `127.0.0.1`, or
can hold API connections for its lifetime, but it creates no cloud resources or persistent local
cache.

On macOS, `sith desktop` runs the same embedded fleet IDE in a native Wails v2 window. It uses an
in-process WebView origin (`wails://wails`), so it does not open a TCP listener. The **Import folder**
control appears only in that window and opens a native directory chooser; it passes the selection to
the identical bounded, in-memory kubeconfig importer used by `sith ui --kubeconfig-dir`. The UI
receives success, cancellation, or a sanitized failure category—never the selected absolute path or
kubeconfig content. Build
an ad-hoc-signed Apple Silicon development bundle with `make desktop-build`; public releases remain
Comment thread
coderabbitai[bot] marked this conversation as resolved.
blocked on Developer ID signing, notarization, stapling, and E9 release provenance, so this is not yet
a distributed replacement for Lens.

Each active lens holds one Kubernetes watch per reachable context after its initial list. A
two-minute safety rediscovery recovers contexts that were offline at launch; it is not the primary
resource refresh path. Very large context/lens counts therefore trade API-server connection and
Expand Down
1 change: 1 addition & 0 deletions cmd/sith-desktop/frontend/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

14 changes: 14 additions & 0 deletions cmd/sith-desktop/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0

// Sith desktop starts the native macOS shell for the local fleet IDE.
package main

import (
"os"

"github.com/ArdurAI/sith/internal/cli"
)

func main() {
os.Exit(cli.ExecuteDesktop())
}
18 changes: 18 additions & 0 deletions cmd/sith-desktop/wails.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "Sith",
"outputfilename": "Sith",
"frontend:dir": "frontend",
"frontend:install": "",
"frontend:build": "true",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"author": {
"name": "ArdurAI",
"email": "security@ardur.ai"
},
"info": {
"companyName": "ArdurAI",
"productName": "Sith",
"productVersion": "0.0.0-dev",
"comments": "Local-first Kubernetes fleet IDE"
}
}
46 changes: 46 additions & 0 deletions docs/adr/0010-native-local-desktop-shell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# ADR 0010: Use a Wails v2 native shell for the local fleet IDE

- Status: Accepted
- Date: 2026-07-14

## Context

Sith already provides a build-free, loopback-only browser IDE through `sith ui`.
Operators also need a macOS application that feels local, including a native folder
chooser for a directory of kubeconfig files. The desktop form must retain the same
source-abstract engine, cache, local-operation boundaries, and privacy posture.

## Decision

Use Wails v2 as a thin macOS shell around the existing Go web UI handler.

- Wails v2 is the upstream stable release line; Wails v3 is alpha and is not used.
- The app serves `webui.Application` through the Wails in-process asset server at
the exact `wails://wails` origin. It opens no TCP listener.
- The existing API handler, strict Host/Origin checks, per-process CSRF capability,
CSP, cache, hydrator, and local operation client remain the only implementation.
- The sole native binding opens a directory chooser. It returns success,
cancellation, or a sanitized failure category to the UI; the selected path
and kubeconfig contents never cross the UI bridge, persist, or enter diagnostics.
- A successful selection builds a new bounded importer session before atomically
replacing the current in-memory session. A failing selection leaves the current
session intact.

## Consequences

The normal CLI remains browser-capable through `sith ui`, while `sith desktop`
opens the same fleet IDE as a local macOS window. `make desktop-build` produces a
development ARM64 `.app` with the stable `com.ardurai.sith` bundle identifier and
an ad-hoc signature. It is deliberately not a public release artifact until E9
supplies Developer ID signing, notarization, stapling, and release provenance.

The first native shell does not add complete Lens parity, telemetry, an updater,
remote control-plane access, Windows/Linux desktop support, or a second Kubernetes
client. Its Wails dependency and macOS runtime therefore become explicit package
review and release-gate responsibilities.

## References

- https://wails.io/docs/introduction/
- https://wails.io/docs/guides/dynamic-assets/
- https://wails.io/docs/guides/signing/
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ decision rests on an external fact, that fact is web-verified and cited (see als
| [0007](0007-local-mcp-transport-auth.md) | Local MCP transport, scope, and authentication | Accepted |
| [0008](0008-deterministic-advisory-brain.md) | Deterministic local advisory brain and evidence contract | Accepted |
| [0009](0009-release-supply-chain.md) | Reproducible and identity-bound release supply chain | Accepted |
| [0010](0010-native-local-desktop-shell.md) | Native local desktop shell | Accepted |

Planning ADRs remain **Proposed** until their implementation lane accepts or rejects them.
Implementation-specific ADRs may be **Accepted** when the corresponding shipped slice provides
Expand Down
22 changes: 22 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
github.com/prometheus/client_golang v1.23.2
github.com/spf13/cobra v1.10.2
github.com/wailsapp/wails/v2 v2.12.0
github.com/zalando/go-keyring v0.2.8
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/term v0.45.0
Expand All @@ -25,7 +26,9 @@ require (
)

require (
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260703014108-f5a850f9c2b7 // indirect
Expand All @@ -40,6 +43,7 @@ require (
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
Expand All @@ -52,27 +56,45 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/labstack/echo/v4 v4.13.3 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
github.com/leaanthony/gosod v1.0.4 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/moby/spdystream v0.5.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.49.1 // indirect
github.com/segmentio/asm v1.1.3 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/tkrajina/go-reflector v0.5.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.21.0 // indirect
Expand Down
Loading