Desktop app to browse and download files from Kubernetes pods
✅ Kubernetes Context Management - View and switch between multiple kubeconfig contexts
✅ Namespace Browsing - Explore all namespaces in your cluster
✅ Pod Selection - View pods and their status with color-coded badges
✅ File System Navigation - Browse pod file systems with breadcrumb navigation
✅ File Download - Download individual files from pods
✅ Dark/Light Mode - Toggle between themes with localStorage persistence
✅ Responsive Design - Works on different screen sizes
✅ Cross-Platform - Supports Linux and Windows pods
✅ Error Handling - Comprehensive error boundaries and user-friendly messages
✅ Performance Optimized - Memoized components and debounced inputs
| # | Feature | Description |
|---|---|---|
| 1 | Context Selection | Pick any Kubernetes context from your local kubeconfig — filterable dropdown with an icon refresh button that reloads the context list |
| 2 | Namespace Browser | View and select namespaces available in the cluster — filterable dropdown with an icon refresh button that reloads the namespace list for the current context |
| 3 | Pod Browser | List pods with status badges (Running / Pending / Failed) and search filter — icon refresh button reloads the pod list for the current namespace |
| 4 | Container Selection | Pods with multiple containers show a container dropdown (hidden for single-container pods); the first container is selected automatically and switching containers reloads the file browser at the container's root |
| 5 | File Explorer | Browse the filesystem of any pod container with a directory table |
| 6 | Filesystem Navigation | Breadcrumb bar, double-click directories, back-button — familiar explorer UX |
| 7 | Directory Refresh | Refresh button in the file explorer reloads the current directory listing without changing the selected pod or navigating away |
| 8 | File Download | Download files via native save dialog using kubectl exec cat (Linux) / cmd /c type (Windows) / tar (minimal Linux images) — binary-safe, streamed directly to disk with no file size cap |
| 9 | Error Dialog | Centralized modal error display (press Esc to dismiss) |
| 10 | Cross-Platform Pods | File listing and download work on Linux (with fallbacks to find/busybox for minimal images) and Windows containers — automatic fallback |
| 11 | Resizable Sidebar | Drag the sidebar edge to resize (200–500px) for better readability |
| 12 | Dark/Light Mode | Toggle between dark and light themes — preference persisted in localStorage. Status text colors (e.g. "Running", "Pending") are theme-aware so they stay readable in every theme, not just dark ones |
| 13 | Error Boundaries | Graceful render-error recovery per component with "Try Again" fallback UI |
| 14 | Pod Details Overlay | An info button in the file explorer opens a resizable overlay (drag the bottom-right corner) with the selected pod's status, node, IP, creation time, controller, and per-container image/ready/restart-count details (press Esc to dismiss) |
| 15 | Volume Mount Info | Each container in the pod details overlay has a collapsible "Mounts" section listing its mount paths and classifying their source (ConfigMap / Secret / PersistentVolumeClaim / HostPath / NFS / CSI are highlighted as coming from "outside" the container image, vs. ephemeral EmptyDir/Projected/DownwardAPI volumes) |
| 16 | Pod Log Download | A button in the file explorer downloads the selected pod/container's logs via kubectl logs through the native save dialog, suggesting <pod-name>-YYYY-MM-DD-HH-mm.log as the file name |
| 17 | Architecture Improvements | Feature-based organization, Zustand state management, service layer abstraction |
📦 Feature-based Organization - Vertical slices for better maintainability 🧠 Zustand State Management - Centralized state with no prop drilling 🔧 Service Layer - Clean abstraction over Electron API calls 📝 Shared Types - Centralized TypeScript types 🚀 Performance Optimized - Memoization, debouncing, code splitting
For detailed architecture information, see ARCHITECTURE.md.
- Electron - Cross-platform desktop framework
- React 19 - Modern UI library with hooks
- TypeScript - Type-safe JavaScript
- Zustand - Lightweight state management
- Vite - Fast build tool
- Tailwind CSS - Utility-first CSS framework
- Vitest - Fast unit testing
- React Testing Library - Component testing
| Channel | Direction | Parameters | Returns |
|---|---|---|---|
get-contexts |
renderer → main | — | ContextInfo[] |
get-namespaces |
renderer → main | contextName |
NamespaceInfo[] |
get-pods |
renderer → main | contextName, namespace |
PodInfo[] |
get-pod-details |
renderer → main | contextName, namespace, podName |
PodDetails | null |
list-files |
renderer → main | contextName, namespace, podName, containerName?, path |
FileEntry[] ← Linux: ls/find/busybox, Windows: cmd /c dir |
show-save-dialog |
renderer → main | defaultName |
string | null |
download-file |
renderer → main | contextName, namespace, podName, containerName?, sourcePath, destPath |
void ← Linux: cat, Windows: cmd /c type |
download-pod-logs |
renderer → main | contextName, namespace, podName, containerName?, destPath |
void ← kubectl logs |
- Node.js ≥ 20
- PNPM ≥ 9 (
npm install -g pnpm) - kubectl — installed and on PATH
- A valid kubeconfig — default
~/.kube/configor setKUBECONFIGenv var
The application validates user-controlled Kubernetes identifiers and container paths before any kubectl command is executed. This blocks traversal-style paths such as /var/log/../etc/passwd and rejects malformed context, namespace, pod, or container names.
The Electron main process keeps sandboxing enabled for the browser window, and renderer code never communicates with kubectl directly. In addition, the renderer uses a safe fallback bridge for Electron API access so the app does not crash when the preload API is unavailable outside a native Electron runtime.
- Distroless / scratch containers (e.g. CoreDNS) contain no shell utilities at all — no
ls,find,busybox,cat, ortar. Because the app browses files bykubectl exec-ing a command, these containers cannot be browsed or downloaded from. The app shows a clear "no supported file listing tool" message instead of a generic error.
# Install dependencies
pnpm install
# Run in development mode (hot-reload)
pnpm electron:dev
# Run tests
pnpm test# Build for current platform
pnpm electron:buildOutput appears in the release/ directory.
pnpm electron:build --winOutput: NSIS installer (.exe)
pnpm electron:build --macOutput: DMG disk image (.dmg)
pnpm electron:build --linuxOutput: AppImage (.AppImage) and Debian package (.deb)
src/
├── app/ # Application shell
│ └── layout/ # Main layout components
├── features/ # Feature-based organization
│ ├── contexts/ # Context selection feature
│ │ ├── components/ # ContextSelector component
│ │ ├── hooks/ # useContexts hook
│ │ └── types/ # Feature-specific types
│ ├── namespaces/ # Namespace selection feature
│ │ ├── components/ # NamespaceSelector component
│ │ ├── hooks/ # useNamespaces hook
│ │ └── types/ # Feature-specific types
│ ├── pods/ # Pod selection feature
│ │ ├── components/ # PodSelector component
│ │ ├── hooks/ # usePods hook
│ │ └── types/ # Feature-specific types
│ ├── filesystem/ # File system browsing feature
│ │ ├── components/ # FileExplorer, FileRow components
│ │ ├── hooks/ # useFileSystem hook
│ │ └── utils/ # File system utilities
│ └── ui/ # Shared UI components
│ ├── components/ # Button, Input, Select, etc.
│ ├── hooks/ # useTheme hook
│ └── types/ # UI-specific types
├── shared/ # Shared code
│ ├── types/ # Centralized TypeScript types
│ │ ├── kubernetes.ts # Kubernetes-related types
│ │ ├── api.ts # API-related types
│ │ ├── errors.ts # Error handling types
│ │ └── index.ts # Type re-exports
│ ├── constants/ # Shared constants
│ │ └── index.ts # UI constants, timeouts, etc.
│ └── utils/ # Utility functions
├── services/ # Service layer
│ └── kubernetesService.ts # Kubernetes service abstraction
├── stores/ # State management
│ ├── kubeStore.ts # Centralized Kubernetes state
│ └── uiStore.ts # UI state management
└── main.tsx # Entry point
electron/
├── main.ts # Electron main process
├── preload.ts # Preload script with type safety
└── kubernetes.ts # Kubernetes operations
The application uses Zustand for centralized state management:
Manages all Kubernetes-related state:
- Contexts (loading, error, selected)
- Namespaces (loading, error, selected)
- Pods (loading, error, selected)
- File system (files, navigation history, loading, error)
- Global error handling
Manages UI-related state:
- Theme (light/dark/system)
- Theme persistence
The KubernetesService provides a clean interface for all Kubernetes operations:
// Example usage
const contexts = await KubernetesService.getContexts();
const namespaces = await KubernetesService.getNamespaces(contextName);
const pods = await KubernetesService.getPods(contextName, namespace);
const podDetails = await KubernetesService.getPodDetails(contextName, namespace, podName);
const files = await KubernetesService.listFiles(contextName, namespace, podName, containerName, dirPath);
await KubernetesService.downloadFile(contextName, namespace, podName, containerName, sourcePath, defaultFileName);
await KubernetesService.downloadPodLogs(contextName, namespace, podName, containerName, defaultFileName);Structured error handling with AppError class:
export enum ErrorCode {
KUBECONFIG_NOT_FOUND = 'KUBECONFIG_NOT_FOUND',
KUBECTL_NOT_INSTALLED = 'KUBECTL_NOT_INSTALLED',
KUBECTL_EXEC_FAILED = 'KUBECTL_EXEC_FAILED',
CONTEXT_NOT_FOUND = 'CONTEXT_NOT_FOUND',
NAMESPACE_NOT_FOUND = 'NAMESPACE_NOT_FOUND',
POD_NOT_FOUND = 'POD_NOT_FOUND',
CONTAINER_NOT_FOUND = 'CONTAINER_NOT_FOUND',
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
PERMISSION_DENIED = 'PERMISSION_DENIED',
NETWORK_ERROR = 'NETWORK_ERROR',
INVALID_INPUT = 'INVALID_INPUT',
INVALID_PATH = 'INVALID_PATH',
TIMEOUT = 'TIMEOUT',
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
}Reusable UI components available in @features/ui/components:
- Button - Primary, secondary, ghost, danger variants
- Input - Form input with validation
- Select - Dropdown select
- Badge - Status indicators
- Tooltip - Hover tooltips
- EmptyState - Empty state placeholders
- ProgressBar - Progress indicators
- LoadingSpinner - Loading animations
- ErrorBoundary - Error boundaries
- ErrorDialog - Global error dialog
- ThemeToggle - Theme switcher
- Context isolation enabled — renderer has no direct Node.js access
- Read-only access — the app only reads files and downloads them; no write operations to the cluster
- No credential storage — reads from the user's existing kubeconfig, does not cache or transmit credentials
- CSP headers — Content Security Policy restricts script and style sources
This version includes significant architecture improvements:
- Restructured codebase into vertical slices (features)
- Each feature is self-contained with components, hooks, and types
- Better separation of concerns and maintainability
- Replaced individual React hooks with Zustand stores
- Single source of truth for all application state
- No prop drilling, better performance
- Created
KubernetesServicefor all Kubernetes operations - Clean interface for API calls
- Easier to mock and test
- Centralized all TypeScript types
- Eliminated type duplication
- Better type safety across the application
- Added
AppErrorclass with error codes - Consistent error messages and handling
- Better debugging capabilities
- Added memoization to key components
- Implemented debouncing for search inputs
- Added code splitting for large dependencies
- Created memoized
FileRowcomponent
- Created comprehensive UI component library
- Consistent styling and behavior
- Better accessibility
- Added path aliases for easier imports
- Configured source maps for better debugging
- Added manual code splitting
- Improved Vite configuration
For developers migrating from the original architecture:
-
Update imports to use feature-based paths:
// Before import { ContextSelector } from "../components/ContextSelector"; // After import { ContextSelector } from "@features/contexts/components/ContextSelector";
-
Replace hooks with store-based approach:
// Before const ctx = useKubeConfig(); // After const ctx = useContexts();
-
Use service layer instead of direct API calls:
// Before const contexts = await fetchContexts(); // After const contexts = await KubernetesService.getContexts();
MIT