# kpf - A TUI for port-forwarding with kubectl

This is a Python utility that (attempts to) dramatically improve the experience of port-forwarding with kubectl.

It is essentially a wrapper around `kubectl port-forward` that adds an interactive service selection with automatic reconnects when the pods are restarted or your network connection is interrupted (computer goes to sleep, etc).

This should be compatible with the `kpf` alias that you may already have.

If you like this, check out <https://github.com/jessegoodier/kdebug>, a TUI for debug containers in Kubernetes pods with interactive shell access and backup capabilities.

## Demo

Demo of the TUI and the reconnect when a pod is restarted:
![kpf demo](docs/kpf-demo_tui.gif)

## Features

- 🔄 **Automatic Restart**: Monitors endpoint changes and restarts port-forward automatically
- 🛡️ **Network Watchdog**: Detects zombie connections after laptop sleep/wake and auto-recovers
- 🎯 **Interactive Selection**: Choose services with a colorful, intuitive interface
- 🌈 **Color-coded Status**: Green for services with endpoints, red for those without
- 🔍 **Multi-resource Support**: Services, pods, deployments, etc.
- 🔐 **Smart Port Handling**: Automatically detects privileged port issues (< 1024) and suggests alternatives

## Installation

**Note**: The `oh-my-zsh` kubectl plugin will conflict with this `kpf` command. You must unalias `kpf` before using this tool.

```sh
echo "unalias kpf 2>/dev/null" >> ~/.zshrc
```

### Homebrew (Recommended)

Other methods do not automatically install command completions.

```bash
brew tap jessegoodier/kpf
brew install kpf
```

Or install directly:

```bash
brew install jessegoodier/kpf/kpf
```

### Using uv

```bash
uv tool install kpf
```

from source:

```bash
uv tool install .
```

## Usage

### Interactive Mode (Recommended)

**Warm Tip**: You can use the interactive mode to find the service you want, and it will output the command to connect to that service directly next time.

**Note**: You might think that "warm tip" is something that AI wrote, but that's not the case. It really is just a little bit cooler than a hot tip.

Visual explanation of the features:
![screenshot1](docs/kpf-tui.png)

Check which endpoints are up on entire cluster (can be slow):
![screenshot2](docs/kpf-demo_check-all.png)

Select services interactively:

Interactive selection in current namespace:

```bash
kpf
```

Interactive selection in specific namespace:

```bash
kpf -n production
```

Interactive selection with namespace prompt:

```bash
kpf -p
```

Show all services across all namespaces:

```bash
kpf --all
```

Include pods and controllers with ports defined:

```bash
kpf --all-ports
```

Combine a few options (interactive mode, all services, and endpoint status checking, debug mode):

```bash
kpf -pAdl
```

### Check Mode

Add endpoint status checking to service selection (slower but shows endpoint health):

```bash
# Interactive selection with endpoint status
kpf --check

# Show all services with endpoint status
kpf --all --check

# Include pods and deployments with status
kpf --all-ports --check
```

### Legacy Mode

Direct port-forward (maintain expected behavior):

```bash
# Traditional kubectl port-forward syntax
kpf svc/frontend 8080:8080 -n production
kpf pod/my-pod 3000:3000
```

### Command Options

```sh

Example usage:
  kpf                                           # Interactive mode
  kpf svc/frontend 8080:8080 -n production      # Direct port-forward (maintain expected behavior)
  kpf -n production                             # Interactive selection in specific namespace
  kpf --all (or -A)                             # Show all services across all namespaces
  kpf --all-ports (or -l)                       # Show all services with their ports
  kpf --check -n production                     # Interactive selection with endpoint status
  kpf --prompt-namespace (or -p)                # Interactive namespace selection
  kpf -z                                        # Listen on 0.0.0.0 (all interfaces)
```

## Examples

### Interactive Service Selection

Fast mode (without endpoint checking):

```bash
$ kpf -n kube-system

Services in namespace: kube-system

#    Type     Name                    Ports
1    SERVICE  kube-dns               53, 9153
2    SERVICE  metrics-server         443
3    SERVICE  kubernetes-dashboard   443

Select a service [1]: 1
Local port (press Enter for 53): 5353
```

With endpoint status checking:

```bash
$ kpf --check -n kube-system

Services in namespace: kube-system

#    Type     Name                    Ports           Status
1    SERVICE  kube-dns               53, 9153         ✓
2    SERVICE  metrics-server         443              ✓
3    SERVICE  kubernetes-dashboard   443              ✗

✓ = Has endpoints  ✗ = No endpoints

Select a service [1]: 1
Local port (press Enter for 53): 5353
```

### Cross-Namespace Discovery

```bash
$ kpf --all

Services across all namespaces

#    Namespace    Type     Name           Ports        Status
1    default      SERVICE  kubernetes     443          ✓
2    kube-system  SERVICE  kube-dns      53, 9153     ✓
3    production   SERVICE  frontend      80, 443      ✓
4    production   SERVICE  backend       8080         ✗
```

### Smart Low Port Handling

When you try to use privileged ports (< 1024), `kpf` will detect the permission issue and offer to use a higher port automatically:

```bash
$ kpf -n monitoring svc/grafana 80:80

Error: Port 80 requires elevated privileges (root/sudo)
Low ports (< 1024) require administrator permissions on most systems

Suggested alternative: Use port 1080 instead?
This would forward: localhost:1080 -> service:80

Use suggested port? [Y/n]: y
Updated port mapping to 1080:80

Direct command: kpf svc/grafana 1080:80 -n monitoring

http://localhost:1080

🚀 port-forward started 🚀
```

This feature prevents confusing "port already in use" errors when the real issue is insufficient permissions.

## How It Works

1. **Port-Forward Thread**: Runs kubectl port-forward in a separate thread
2. **Endpoint Watcher**: Monitors endpoint changes using `kubectl get ep -w`
3. **Network Watchdog**: Checks both K8s API connectivity and local port health every 5 seconds to detect zombie connections (e.g., after laptop sleep/wake). This catches cases where the API is reachable but the port-forward tunnel is dead.
4. **Automatic Restart**: When endpoints change or connectivity is lost, gracefully restarts the port-forward
5. **Service Discovery**: Uses kubectl to discover services and their endpoint status

## Requirements

- kubectl configured with cluster access

## Configuration

kpf can be configured via `~/.config/kpf/kpf.json` (follows [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)).

If you create this file, it is suggested to only change the values you want to override in case improvements are made in the future.

```json
{
  "autoSelectFreePort": true,
  "showDirectCommand": true,
  "showDirectCommandIncludeContext": true,
  "directCommandMultiLine": true,
  "autoReconnect": true,
  "reconnectAttempts": 30,
  "reconnectDelaySeconds": 5,
  "captureUsageDetails": false,
  "usageDetailFolder": "${HOME}/.config/kpf/usage-details",
  "restartThrottleSeconds": 5,
  "networkWatchdogEnabled": true,
  "networkWatchdogInterval": 5,
  "networkWatchdogFailureThreshold": 2
}
```

Example: Disable auto-reconnect

```sh
mkdir -p ~/.config/kpf
echo '{"autoReconnect": false}' > ~/.config/kpf/kpf.json
```

### Configuration Options

| Option                            | Type    | Default                             | Description                                                                  |
| --------------------------------- | ------- | ----------------------------------- | ---------------------------------------------------------------------------- |
| `autoSelectFreePort`              | boolean | `true`                              | When requested port is busy, automatically try next ports (9091, 9092, etc.) |
| `showDirectCommand`               | boolean | `true`                              | Show the direct `kpf` command for future use                                 |
| `showDirectCommandIncludeContext` | boolean | `true`                              | Include kubectl context in the command display                               |
| `directCommandMultiLine`          | boolean | `true`                              | Format direct command across multiple lines for readability                  |
| `autoReconnect`                   | boolean | `true`                              | Automatically reconnect when connection drops                                |
| `reconnectAttempts`               | integer | `30`                                | Number of reconnection attempts before giving up                             |
| `reconnectDelaySeconds`           | integer | `5`                                 | Delay in seconds between reconnection attempts                               |
| `captureUsageDetails`             | boolean | `false`                             | Capture usage details locally for debugging (not sent anywhere)              |
| `usageDetailFolder`               | string  | `${HOME}/.config/kpf/usage-details` | Where to store usage detail logs                                             |
| `networkWatchdogEnabled`          | boolean | `true`                              | Monitor K8s API connectivity to detect zombie connections                    |
| `networkWatchdogInterval`         | integer | `5`                                 | Seconds between connectivity checks                                          |
| `networkWatchdogFailureThreshold` | integer | `2`                                 | Consecutive failures before triggering restart                               |

**Notes:**

- All settings are optional - kpf will use defaults if the config file doesn't exist
- Environment variables like `${HOME}` are expanded automatically
- The config file location respects the `XDG_CONFIG_HOME` environment variable
- Invalid JSON or unknown keys will show warnings but won't prevent kpf from running
- CLI arguments override config file values when provided

## Development

### Prerequisites

- [uv](https://github.com/astral-sh/uv)
- [just](https://github.com/casey/just)

### Setup Development Environment

```bash
# Clone the repository
git clone https://github.com/jessegoodier/kpf.git
cd kpf
```

```bash
# Install with development dependencies and create venv
just dev-setup
```

### Code Quality Tools

```bash
# Format and lint code
just format
```

```bash
# Run tests
just test
```

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests and linting
5. Submit a pull request

## Shell Completion

Shell completions can be generated using the `--completions` flag.

### Homebrew

If you install via Homebrew, completions should be installed automatically. You may need to follow Homebrew's [shell completion instructions](https://docs.brew.sh/Shell-Completion) to ensure it's loaded. You may find the bash and zsh examples [here](https://github.com/jessegoodier/toolbox/tree/main/homebrew) useful.

### Manual Installation

#### Bash

```bash
# User-local installation (recommended)
kpf --completions bash > ~/.local/share/bash-completion/completions/kpf

# Or system-wide
kpf --completions bash | sudo tee /etc/bash_completion.d/kpf > /dev/null
```

#### Zsh

```zsh
# Add to a directory in your fpath
kpf --completions zsh > /usr/share/zsh/site-functions/_kpf

# Or for oh-my-zsh users
kpf --completions zsh > ~/.oh-my-zsh/completions/_kpf
```

Then reload your shell: `exec $SHELL`

## License

MIT License - see [LICENSE](LICENSE) file for details.

<p align="center">
  <a href="https://www.buymeacoffee.com/jessegoodier">
    <img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=jessegoodier&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff" />
  </a>
</p>
