mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-10-12 04:35:10 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3471502b7a | ||
|
|
17f9248588 | ||
|
|
2751b02428 | ||
|
|
7e08fd7758 |
@@ -8,51 +8,99 @@
|
|||||||
<a href="https://goreportcard.com/report/github.com/lukaszraczylo/kportal"><img src="https://goreportcard.com/badge/github.com/lukaszraczylo/kportal" alt="Go Report Card"></a>
|
<a href="https://goreportcard.com/report/github.com/lukaszraczylo/kportal"><img src="https://goreportcard.com/badge/github.com/lukaszraczylo/kportal" alt="Go Report Card"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
# kportal
|
||||||
<strong>Kubernetes port-forward manager with interactive terminal UI</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
kportal manages multiple Kubernetes port-forwards with an interactive terminal interface. It provides real-time status updates, automatic reconnection, hot-reload configuration, and mDNS hostname publishing.
|
kportal runs many Kubernetes port-forwards from one YAML file and shows their status in a terminal UI. It reconnects dropped forwards, reloads the file when it changes, and can run without the UI.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## ✨ Features
|
Screenshot from kportal v0.2.17. The current key bar also lists `PgUp`/`PgDn`, `b` (Bench) and `l` (Logs).
|
||||||
|
|
||||||
- **Interactive TUI** - Terminal interface with keyboard navigation
|
Website: <https://lukaszraczylo.github.io/kportal>
|
||||||
- **Live management** - Add, edit, and delete port-forwards without restarting
|
|
||||||
- **Auto-reconnect** - Exponential backoff retry on connection failures
|
|
||||||
- **Hot-reload** - Configuration changes applied automatically
|
|
||||||
- **Health monitoring** - Multiple check methods with stale connection detection
|
|
||||||
- **Multi-context** - Support for multiple Kubernetes contexts and namespaces
|
|
||||||
- **Pod restart handling** - Automatic reconnection when pods restart
|
|
||||||
- **Label selectors** - Dynamic pod targeting using label selectors
|
|
||||||
- **Port conflict detection** - Validates port availability with PID information
|
|
||||||
- **mDNS hostnames** - Access forwards via `.local` hostnames
|
|
||||||
- **HTTP traffic logging** - Real-time HTTP request/response logging for debugging
|
|
||||||
- **Connection benchmarking** - Built-in HTTP benchmarking with latency statistics
|
|
||||||
- **Headless mode** - Background operation for scripting and automation
|
|
||||||
|
|
||||||
## 🔄 Comparison with Other Tools
|
## Contents
|
||||||
|
|
||||||
| Feature | kportal | [k9s](https://k9scli.io/) | [Kube Forwarder](https://kube-forwarder.pixelpoint.io/) | [kftray](https://kftray.app/) |
|
- [Features](#features)
|
||||||
|---------|---------|------|----------------|--------|
|
- [Comparison](#comparison-with-other-tools)
|
||||||
| **Interface** | Terminal TUI | Terminal TUI | Desktop GUI (Electron) | Desktop GUI + TUI |
|
- [Installation](#installation)
|
||||||
| **Persistent Config** | ✅ YAML file | ❌ Session only | ✅ JSON bookmarks | ✅ JSON + Git sync |
|
- [Quick start](#quick-start)
|
||||||
| **Auto-reconnect** | ✅ Exponential backoff | ❌ Manual | ✅ Basic | ✅ Watch API |
|
- [Configuration](#configuration)
|
||||||
| **Hot-reload Config** | ✅ File watch + SIGHUP | ❌ | ❌ | ❌ |
|
- [Usage](#usage)
|
||||||
| **Health Checks** | ✅ TCP + data-transfer | ❌ | ❌ | ❌ |
|
- [Status indicators](#status-indicators)
|
||||||
| **Stale Connection Detection** | ✅ Age + idle tracking | ❌ | ❌ | ❌ |
|
- [Advanced features](#advanced-features)
|
||||||
| **HTTP Traffic Logging** | ✅ Built-in viewer | ❌ | ❌ | ✅ |
|
- [Migration from kftray](#migration-from-kftray)
|
||||||
| **Connection Benchmarking** | ✅ Built-in | ✅ Via Hey | ❌ | ❌ |
|
- [Signal handling](#signal-handling)
|
||||||
| **mDNS Hostnames** | ✅ `.local` domains | ❌ | ❌ | ❌ |
|
- [Troubleshooting](#troubleshooting)
|
||||||
| **Label Selectors** | ✅ | ✅ | ❌ | ✅ |
|
- [Development](#development)
|
||||||
| **Multi-context** | ✅ | ✅ | ✅ | ✅ |
|
- [Telemetry](#telemetry)
|
||||||
| **Headless Mode** | ✅ | ❌ | ❌ | ❌ |
|
- [License](#license)
|
||||||
| **System Tray** | ❌ | ❌ | ❌ | ✅ |
|
|
||||||
| **UDP Support** | ❌ | ❌ | ❌ | ✅ Proxy relay |
|
|
||||||
| **Dependencies** | Single binary | Single binary | Electron | Tauri + kubectl |
|
|
||||||
|
|
||||||
## 📦 Installation
|
## Features
|
||||||
|
|
||||||
|
| Area | What kportal does |
|
||||||
|
|------|-------------------|
|
||||||
|
| Forwards | Forwards to `service/<name>`, `pod/<name>` (exact name or name prefix), or `pod` with a label `selector`. Only TCP is supported. |
|
||||||
|
| Terminal UI | Add (`n`), edit (`e`), delete (`d`) and toggle (`Space`) forwards without a restart. The add wizard writes the config file. |
|
||||||
|
| Table columns | `tui.columns` sets which columns the forwards table shows, their order and their width. It applies to the UI and to the `-v` table. |
|
||||||
|
| Add wizard | If the cluster does not allow listing namespaces, the wizard asks you to type the namespace. |
|
||||||
|
| Reconnect | Retries with exponential backoff: 1s, 2s, 4s, 8s, then 10s, with 10% jitter. It does not stop retrying. |
|
||||||
|
| Pod restarts | Forwards by pod prefix or selector connect to a new pod after a restart. |
|
||||||
|
| Health checks | `tcp-dial` or `data-transfer` check every 3s by default. A connection is stale when it is older than `maxConnectionAge` and idle, or idle longer than `maxIdleTime`. |
|
||||||
|
| Hot reload | The file watcher and `SIGHUP` reload the config. An invalid config is rejected and the previous one stays active. |
|
||||||
|
| Contexts | Several Kubernetes contexts and namespaces in one file. `--context` selects which contexts run. |
|
||||||
|
| Port conflicts | Startup and reload checks report a busy local port and the process that holds it. |
|
||||||
|
| mDNS | With `mdns.enabled: true`, each forward is reachable as `<alias>.local`. Without an alias, the resource name is used. |
|
||||||
|
| HTTP log | Per-forward request and response log in the UI, with detail view, filter, search, JSON formatting, gzip/deflate decoding and clipboard copy. |
|
||||||
|
| Header redaction | With `includeHeaders: true`, values of sensitive headers such as `Authorization` and `Cookie` become `[REDACTED]`. You cannot turn this off. |
|
||||||
|
| Benchmark | Press `b` to send HTTP requests through a forward. Reports success and failure counts, min/max/avg latency, P50/P95/P99, requests per second and status codes. |
|
||||||
|
| Generate | `kportal generate` lists services in a cluster and appends the ones you pick to the config with consecutive local ports. |
|
||||||
|
| Headless | `-headless` runs without the UI and logs to stderr. `-log-format json` switches the log format. |
|
||||||
|
| Import | `-convert` turns a kftray JSON file into a kportal YAML file. |
|
||||||
|
| Shell completion | `kportal completion` prints a bash, zsh or fish completion script, or installs it. |
|
||||||
|
| Verified installer | `install.sh` checks the archive SHA-256 and, if `cosign` is on `PATH`, the keyless signature of the checksums file. |
|
||||||
|
| Updates | `-update` checks for a newer release. `-version` prints the version. |
|
||||||
|
|
||||||
|
## Comparison with other tools
|
||||||
|
|
||||||
|
Compared on 2026-10-02 against each project's README and docs. Check their repositories for changes. The kportal column comes from this repository's code. "Not documented" means the project's README and docs do not say.
|
||||||
|
|
||||||
|
| Feature | kportal | kubectl port-forward | k9s | kubefwd | kftray |
|
||||||
|
|---------|---------|----------------------|-----|---------|--------|
|
||||||
|
| Many forwards in one config file | Yes | No | Partial (annotations configure forwards; no forward list file) | Partial (`example.fwdconf.yml` exists) | Yes |
|
||||||
|
| Automatic reconnect | Yes | Not documented | Not documented | Yes | Yes |
|
||||||
|
| Follows pod restarts by label or name prefix | Yes | Not documented | Not documented | Yes (label filter) | Yes |
|
||||||
|
| Terminal UI of all forwards | Yes | No | Yes (PortForward view, alias `pf`; forwards last for the session) | Yes | Yes |
|
||||||
|
| GUI | No | No | No | No | Yes |
|
||||||
|
| Config hot reload | Yes (file watcher, `SIGHUP`) | No | Partial (`benchmarks.yaml` only) | Not documented | Not documented |
|
||||||
|
| HTTP request and response logging | Yes | No | Not documented | Not documented | Yes |
|
||||||
|
| Health checks | Yes (`tcp-dial`, `data-transfer`) | Not documented | Not documented | Not documented | Partial (README says it tracks pod health) |
|
||||||
|
| mDNS or friendly hostnames | Yes (mDNS) | No | No | Partial (edits `/etc/hosts`, not mDNS; needs root) | Not documented |
|
||||||
|
| Built-in benchmark | Yes | No | Yes (`hey`, key `b`) | Not documented | Not documented |
|
||||||
|
| Imports another tool's config | Yes (kftray JSON) | No | No | Not documented | Partial (JSON, GitHub repos, service annotations; not other tools' formats) |
|
||||||
|
| Several contexts in one config | Yes | No | Partial | Partial | Not documented |
|
||||||
|
| Local port conflict detection | Yes (reports the holding process) | Partial (`:remotePort` picks a free port) | Not documented | Yes | Not documented |
|
||||||
|
| Platforms | Linux, macOS, Windows | Not documented | macOS, Linux, Windows | macOS, Linux, Windows | macOS, Linux, Windows |
|
||||||
|
| Licence | MIT | Not documented | Apache-2.0 | Apache-2.0 | GPL-3.0 |
|
||||||
|
| Language | Go | Not documented | Go | Go | Rust (Tauri, Ratatui) |
|
||||||
|
| Latest release (2026-10-02) | [Releases](https://github.com/lukaszraczylo/kportal/releases) | Not documented | v0.51.0 (2026-06-06) | v1.25.16 (2026-06-20) | v0.29.2 (2026-09-29) |
|
||||||
|
|
||||||
|
When another tool fits better:
|
||||||
|
|
||||||
|
- kubectl port-forward for a one-off forward. It needs no config file and ships with kubectl.
|
||||||
|
- k9s for general cluster browsing and its built-in benchmark.
|
||||||
|
- kubefwd to forward every service in a namespace with local DNS names.
|
||||||
|
- kftray for a tray GUI, shared JSON configs and UDP.
|
||||||
|
|
||||||
|
Kube Forwarder is a GUI-only forwarder. Its last release was 2019-08-14.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
|
||||||
|
- kubectl: [Use Port Forwarding to Access Applications in a Cluster](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)
|
||||||
|
- k9s: <https://github.com/derailed/k9s> and <https://k9scli.io/topics/bench/>
|
||||||
|
- kubefwd: <https://github.com/txn2/kubefwd>
|
||||||
|
- kftray: <https://github.com/hcavarsan/kftray>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
### Homebrew (macOS)
|
### Homebrew (macOS)
|
||||||
|
|
||||||
@@ -60,29 +108,37 @@ kportal manages multiple Kubernetes port-forwards with an interactive terminal i
|
|||||||
brew install --cask lukaszraczylo/taps/kportal
|
brew install --cask lukaszraczylo/taps/kportal
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**: If you previously installed via `brew install lukaszraczylo/taps/kportal` (formula), uninstall first:
|
If you installed the formula (`brew install lukaszraczylo/taps/kportal`) earlier, run `brew uninstall kportal` first.
|
||||||
> ```bash
|
|
||||||
> brew uninstall kportal
|
|
||||||
> ```
|
|
||||||
|
|
||||||
### Quick Install
|
### Quick install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer downloads `kportal-<version>-checksums.txt` from the same release and verifies the archive's SHA-256 before installing. If [`cosign`](https://github.com/sigstore/cosign) is on your `PATH`, the checksums file's keyless cosign signature is also verified against the shared-actions reusable workflow identity.
|
The script downloads `kportal-<version>-checksums.txt` from the same release and verifies the archive SHA-256. If [`cosign`](https://github.com/sigstore/cosign) is on `PATH`, it also verifies the keyless signature of the checksums file.
|
||||||
|
|
||||||
| Variable | Effect |
|
| Variable | Effect |
|
||||||
|----------|--------|
|
|----------|--------|
|
||||||
| `DRY_RUN=1` | Download and verify only; do not install |
|
| `DRY_RUN=1` | Download and verify only. Do not install. |
|
||||||
| `SKIP_COSIGN=1` | Skip cosign signature verification (SHA-256 is still enforced) |
|
| `SKIP_COSIGN=1` | Skip the cosign check. The SHA-256 check still runs. |
|
||||||
|
| `INSTALL_DIR` | Target directory. Default `/usr/local/bin`. |
|
||||||
|
| `KPORTAL_VERSION` | Install a specific version, for example `1.2.3`, instead of the latest. |
|
||||||
|
| `COSIGN_CERT_IDENTITY_REGEXP` | Certificate identity to accept. Set it if you fork the release pipeline. |
|
||||||
|
|
||||||
### Manual Download
|
Example:
|
||||||
|
|
||||||
Download binaries from the [releases page](https://github.com/lukaszraczylo/kportal/releases).
|
```bash
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/kportal/main/install.sh | DRY_RUN=1 bash
|
||||||
|
```
|
||||||
|
|
||||||
### Build from Source
|
### Manual download
|
||||||
|
|
||||||
|
The [releases page](https://github.com/lukaszraczylo/kportal/releases) has archives for Linux, macOS and Windows on amd64 and arm64. Archives are named `kportal-<version>-<os>-<arch>.tar.gz`, and `.zip` on Windows. Each release also has a `kportal-<version>-checksums.txt` file.
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
|
||||||
|
Go 1.26 or later is required (see `go.mod`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/lukaszraczylo/kportal.git
|
git clone https://github.com/lukaszraczylo/kportal.git
|
||||||
@@ -90,12 +146,13 @@ cd kportal
|
|||||||
make build && make install
|
make build && make install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Verifying Release Signatures
|
`make install` copies the binary to `/opt/homebrew/bin` if that directory exists, otherwise to `~/.local/bin` on macOS and Linux.
|
||||||
|
|
||||||
All release checksums are signed with [cosign](https://github.com/sigstore/cosign) using keyless signing. To verify:
|
### Verifying release signatures
|
||||||
|
|
||||||
|
Release checksums are signed with [cosign](https://github.com/sigstore/cosign) using keyless signing. Download the checksums file and its sigstore bundle from the release, then run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download the checksum file and its sigstore bundle from the release
|
|
||||||
cosign verify-blob \
|
cosign verify-blob \
|
||||||
--certificate-identity-regexp "^https://github\.com/lukaszraczylo/shared-actions/\.github/workflows/go-release\.yaml@refs/heads/main$" \
|
--certificate-identity-regexp "^https://github\.com/lukaszraczylo/shared-actions/\.github/workflows/go-release\.yaml@refs/heads/main$" \
|
||||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||||
@@ -103,7 +160,9 @@ cosign verify-blob \
|
|||||||
kportal-<version>-checksums.txt
|
kportal-<version>-checksums.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 Quick Start
|
Replace `<version>` with the release version, for example `1.2.3`. Then check the archive against the verified checksums file with `shasum -a 256` or `sha256sum`.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
Create `.kportal.yaml`:
|
Create `.kportal.yaml`:
|
||||||
|
|
||||||
@@ -124,31 +183,40 @@ contexts:
|
|||||||
port: 8080
|
port: 8080
|
||||||
localPort: 8080
|
localPort: 8080
|
||||||
alias: api
|
alias: api
|
||||||
httpLog: true # Enable HTTP traffic logging
|
httpLog: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Each `name` under `contexts` must exist in your kubeconfig. List them with `kubectl config get-contexts`.
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal
|
kportal
|
||||||
```
|
```
|
||||||
|
|
||||||
### Keyboard Controls
|
### Keyboard controls
|
||||||
|
|
||||||
|
Forwards list:
|
||||||
|
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|-----|--------|
|
|-----|--------|
|
||||||
| `↑↓` / `j/k` | Navigate |
|
| `Up` `Down` / `j` `k` | Move the selection |
|
||||||
| `Space` / `Enter` | Toggle forward |
|
| `PgUp` `PgDn` / `Ctrl+u` `Ctrl+d` | Move by 10 rows |
|
||||||
| `n` | Add new forward |
|
| `Space` / `Enter` | Toggle the forward |
|
||||||
| `e` | Edit forward |
|
| `n` | Add a forward |
|
||||||
| `d` | Delete forward |
|
| `e` | Edit the forward |
|
||||||
| `b` | Benchmark connection |
|
| `d` | Delete the forward (asks to confirm) |
|
||||||
| `l` | View HTTP logs |
|
| `b` | Benchmark the forward |
|
||||||
| `q` | Quit |
|
| `l` | Open the HTTP log |
|
||||||
|
| `q` / `Ctrl+c` | Quit |
|
||||||
|
|
||||||
## 📖 Configuration
|
In the add and edit wizard, `h` on the confirmation step toggles `httpLog`. Advanced `httpLog` keys you set in YAML stay unchanged.
|
||||||
|
|
||||||
### Basic Structure
|
HTTP log keys are listed under [HTTP traffic logging](#http-traffic-logging).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Basic structure
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
contexts:
|
contexts:
|
||||||
@@ -162,56 +230,56 @@ contexts:
|
|||||||
localPort: <local-port>
|
localPort: <local-port>
|
||||||
alias: <display-name> # optional
|
alias: <display-name> # optional
|
||||||
selector: <label-selector> # optional
|
selector: <label-selector> # optional
|
||||||
httpLog: true # optional - enable HTTP logging
|
httpLog: true # optional
|
||||||
```
|
```
|
||||||
|
|
||||||
### Forward Options
|
### Forward options
|
||||||
|
|
||||||
| Field | Required | Description |
|
| Field | Required | Description |
|
||||||
|-------|----------|-------------|
|
|-------|----------|-------------|
|
||||||
| `resource` | Yes | Resource type and name (e.g., `service/postgres`, `pod/my-app`) |
|
| `resource` | Yes | `service/<name>`, `pod/<name>`, or `pod` with `selector` |
|
||||||
| `protocol` | Yes | Protocol (`tcp`) |
|
| `protocol` | No | `tcp`. No other value is accepted. |
|
||||||
| `port` | Yes | Remote port |
|
| `port` | Yes | Remote port |
|
||||||
| `localPort` | Yes | Local port |
|
| `localPort` | Yes | Local port |
|
||||||
| `alias` | No | Display name and mDNS hostname |
|
| `alias` | No | Display name and mDNS hostname |
|
||||||
| `selector` | No | Label selector for pod resolution |
|
| `selector` | With bare `pod` | Label selector. Not allowed with `pod/<name>`. |
|
||||||
| `httpLog` | No | Enable HTTP traffic logging (`true`/`false`) |
|
| `httpLog` | No | `true`, or a map of options (see [HTTP traffic logging](#http-traffic-logging)) |
|
||||||
|
|
||||||
### Resource Formats
|
### Resource formats
|
||||||
|
|
||||||
| Format | Description |
|
| Format | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `service/name` | Service forwarding |
|
| `service/name` | Forward to a service |
|
||||||
| `pod/name` | Direct pod by name |
|
| `pod/name` | Forward to a pod by exact name, or by name prefix |
|
||||||
| `pod/prefix` | Pod by prefix (matches `prefix-*`) |
|
| `pod` with `selector` | Forward to a pod picked by label selector |
|
||||||
| `pod` + `selector` | Pod by label selector |
|
|
||||||
| `deployment/name` | Deployment |
|
|
||||||
|
|
||||||
### Health Check Configuration
|
Valid resource types are `pod` and `service`. To reach a deployment, use `pod` with a `selector`.
|
||||||
|
|
||||||
|
### Health checks and reliability
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
healthCheck:
|
healthCheck:
|
||||||
interval: "3s" # Check frequency
|
interval: "3s"
|
||||||
timeout: "2s" # Check timeout
|
timeout: "2s"
|
||||||
method: "data-transfer" # tcp-dial or data-transfer
|
method: "data-transfer" # or tcp-dial
|
||||||
maxConnectionAge: "25m" # Reconnect before k8s timeout
|
maxConnectionAge: "25m"
|
||||||
maxIdleTime: "10m" # Detect idle connections
|
maxIdleTime: "10m"
|
||||||
|
|
||||||
reliability:
|
reliability:
|
||||||
tcpKeepalive: "30s"
|
tcpKeepalive: "30s"
|
||||||
dialTimeout: "30s"
|
dialTimeout: "30s"
|
||||||
retryOnStale: true
|
retryOnStale: true
|
||||||
|
watchdogPeriod: "30s"
|
||||||
```
|
```
|
||||||
|
|
||||||
Health check methods:
|
The values above are the defaults from `.kportal.yaml`.
|
||||||
- `tcp-dial` - Fast TCP connection test
|
|
||||||
- `data-transfer` - Verifies tunnel functionality by attempting data read
|
|
||||||
|
|
||||||
Connection age reconnection only triggers when the connection is also idle, preventing interruption of active transfers like database dumps.
|
- `tcp-dial` opens a TCP connection to the local port.
|
||||||
|
- `data-transfer` also tries to read data through the tunnel. It is the default.
|
||||||
|
- A connection past `maxConnectionAge` reconnects only when it is also idle, so a running transfer is not cut.
|
||||||
|
- `watchdogPeriod` is the interval of the check for hung workers.
|
||||||
|
|
||||||
### mDNS Hostnames
|
### mDNS
|
||||||
|
|
||||||
Enable mDNS to access forwards via `.local` hostnames:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
mdns:
|
mdns:
|
||||||
@@ -225,22 +293,22 @@ contexts:
|
|||||||
- resource: service/postgres
|
- resource: service/postgres
|
||||||
port: 5432
|
port: 5432
|
||||||
localPort: 5432
|
localPort: 5432
|
||||||
alias: prod-db # Accessible via prod-db.local:5432
|
alias: prod-db # reachable as prod-db.local:5432
|
||||||
```
|
```
|
||||||
|
|
||||||
- Explicit `alias` becomes `<alias>.local`
|
- `alias: prod-db` publishes `prod-db.local`.
|
||||||
- Without alias, hostname is generated from resource name (`service/redis` → `redis.local`)
|
- Without an alias, `service/redis` publishes `redis.local`.
|
||||||
- Works on macOS (Bonjour) and Linux (avahi-daemon)
|
- A bare `pod` with a selector and no alias is not published.
|
||||||
|
- Services register as `_kportal._tcp`.
|
||||||
|
|
||||||
|
Check registration:
|
||||||
|
|
||||||
Verify registration:
|
|
||||||
```bash
|
```bash
|
||||||
dns-sd -B _kportal._tcp local # macOS
|
dns-sd -B _kportal._tcp local # macOS
|
||||||
avahi-browse -t _kportal._tcp # Linux
|
avahi-browse -t _kportal._tcp # Linux
|
||||||
```
|
```
|
||||||
|
|
||||||
### Table Columns
|
### Table columns
|
||||||
|
|
||||||
Choose which columns the forwards table shows, and in what order. The setting applies to the interactive UI and the `-verbose` table:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
tui:
|
tui:
|
||||||
@@ -252,90 +320,83 @@ tui:
|
|||||||
- name: status
|
- name: status
|
||||||
```
|
```
|
||||||
|
|
||||||
- Valid names: `context`, `namespace`, `alias`, `type`, `resource`, `remote`, `local`, `status`
|
- Valid names: `context`, `namespace`, `alias`, `type`, `resource`, `remote`, `local`, `status`.
|
||||||
- The list order is the render order, and unlisted columns are hidden
|
- List order is render order. Unlisted columns are hidden.
|
||||||
- `width` is the maximum text width, from 1 to 200. If you omit it, the column keeps its default width
|
- `width` is the maximum text width, 1 to 200. Without it, the column keeps its default width.
|
||||||
- If you omit `tui`, the table keeps its default columns and order
|
- Without `tui`, the table keeps its default columns and order.
|
||||||
- Changes apply on hot-reload
|
- The setting applies to the UI and to the `-v` table, and reloads with the file.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Interactive Mode
|
### Command line
|
||||||
|
|
||||||
|
| Command or flag | Effect |
|
||||||
|
|-----------------|--------|
|
||||||
|
| `kportal` | Start the terminal UI. |
|
||||||
|
| `-c <file>` | Config file. Default `.kportal.yaml`. |
|
||||||
|
| `-v` | Verbose logging. In headless mode it sets the log level only. |
|
||||||
|
| `-headless` | No UI. Logs go to stderr. |
|
||||||
|
| `-check` | Validate the config and exit. |
|
||||||
|
| `-context <name>` | Forward only the named contexts. Repeatable and comma-separated. |
|
||||||
|
| `-log-format text\|json` | Log format. Default `text`. |
|
||||||
|
| `-convert <file>` | Convert a kftray JSON file to kportal YAML. |
|
||||||
|
| `-convert-output <file>` | Output of `-convert`. Default `.kportal.yaml`. |
|
||||||
|
| `-update` | Check for updates and exit. |
|
||||||
|
| `-version` | Print the version and exit. |
|
||||||
|
| `kportal generate` | Add forwards from a cluster. |
|
||||||
|
| `kportal completion` | Print or install a shell completion script. |
|
||||||
|
|
||||||
|
Each flag also accepts the double-dash form, for example `--check`.
|
||||||
|
|
||||||
|
### Interactive mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal
|
kportal
|
||||||
```
|
```
|
||||||
|
|
||||||
### Verbose Mode
|
### Verbose mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal -v
|
kportal -v
|
||||||
```
|
```
|
||||||
|
|
||||||
### Headless Mode
|
Without `-headless`, `-v` prints a plain-text table that redraws. The table uses the same status words as the UI. When stdout is not a terminal, it draws `✓ Active`, `⋯ Starting`, `↻ Reconnecting` and `✗ Error`.
|
||||||
|
|
||||||
Run without TUI for scripting and automation:
|
### Headless mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal -headless
|
kportal -headless
|
||||||
```
|
|
||||||
|
|
||||||
Headless mode emits both structured and standard-library logs to stderr by default
|
|
||||||
(suitable for redirecting to a log file or systemd journal). The `-v` flag controls
|
|
||||||
log level (debug vs info), not destination.
|
|
||||||
|
|
||||||
Combines well with verbose mode for background operation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kportal -headless -v 2>kportal.log &
|
kportal -headless -v 2>kportal.log &
|
||||||
|
kportal -headless -log-format json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Validate Configuration
|
Headless mode logs to stderr, so you can redirect it to a file or the systemd journal. `-v` sets the log level, not the destination.
|
||||||
|
|
||||||
|
### Validate configuration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal --check
|
kportal -check
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Config File
|
### Custom config file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal -c /path/to/config.yaml
|
kportal -c /path/to/config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Select Contexts
|
### Select contexts
|
||||||
|
|
||||||
By default every context in the config file is forwarded. `--context` limits the
|
By default kportal forwards every context in the file. `--context` limits the run to the contexts you name:
|
||||||
run to the contexts you name, which is useful when the file describes several
|
|
||||||
clusters but you only work against one at a time:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal --context development-team-a
|
kportal --context development-team-a
|
||||||
```
|
|
||||||
|
|
||||||
The flag is repeatable and accepts comma-separated values, so these are
|
|
||||||
equivalent:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kportal --context team-a --context team-b
|
kportal --context team-a --context team-b
|
||||||
kportal --context team-a,team-b
|
kportal --context team-a,team-b
|
||||||
```
|
```
|
||||||
|
|
||||||
It composes with the other modes, and works well as a per-cluster shell alias:
|
An unknown name is an error that lists the contexts in the file. Those are the `contexts[].name` values in `.kportal.yaml`, not every context in your kubeconfig.
|
||||||
|
|
||||||
```bash
|
Local-port conflicts are only reported between forwards that can run together. With `--context`, contexts you never select together can reuse a `localPort`:
|
||||||
alias kp-a='kportal --context development-team-a'
|
|
||||||
kportal --context development-team-a -headless
|
|
||||||
```
|
|
||||||
|
|
||||||
An unknown context name is an error that lists the contexts the file defines, so
|
|
||||||
a typo fails loudly instead of silently forwarding nothing.
|
|
||||||
|
|
||||||
#### Reusing local ports across contexts
|
|
||||||
|
|
||||||
Local-port conflicts are only reported between forwards that could run at the
|
|
||||||
same time. Contexts that are never selected together may therefore reuse the
|
|
||||||
same `localPort`, so the same service keeps the same local port in every
|
|
||||||
cluster and the app you are developing never needs reconfiguring:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
contexts:
|
contexts:
|
||||||
@@ -357,15 +418,9 @@ contexts:
|
|||||||
- <<: *rate-service # same local port, no conflict
|
- <<: *rate-service # same local port, no conflict
|
||||||
```
|
```
|
||||||
|
|
||||||
This requires passing `--context`. Without it every context is active, so the
|
Without `--context`, or with both contexts named, the duplicate port is an error.
|
||||||
duplicate ports above would genuinely collide and are still reported as an
|
|
||||||
error - as is naming both contexts at once (`--context team-a,team-b`).
|
|
||||||
|
|
||||||
### Generate Forwards from a Cluster
|
### Generate
|
||||||
|
|
||||||
The `generate` subcommand discovers services in a Kubernetes context and lets you
|
|
||||||
interactively pick which ones to forward. Selected entries are appended to the
|
|
||||||
config file with consecutive local ports starting from a value you choose.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kportal generate --context=my-cluster
|
kportal generate --context=my-cluster
|
||||||
@@ -375,97 +430,51 @@ kportal generate --context=my-cluster --dry-run
|
|||||||
|
|
||||||
| Flag | Description |
|
| Flag | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `--context` | (required) Kubernetes context to scan |
|
| `--context` | Required. Kubernetes context to scan. |
|
||||||
| `--config` | Path to kportal config file (default: `.kportal.yaml`) |
|
| `--config` | Config file to append to. Default `.kportal.yaml`. |
|
||||||
| `--dry-run` | Print the planned forwards but do not modify the config |
|
| `--dry-run` | Print the planned forwards. Do not change the config. |
|
||||||
|
|
||||||
The interactive flow has three steps:
|
Steps:
|
||||||
|
|
||||||
1. **Namespaces** — multi-select with `space`, toggle-all with `a`, filter with `/`.
|
1. Namespaces: select with `space`, toggle all with `a`, filter with `/`.
|
||||||
2. **Services** — same controls; rows already present in the config are locked off, and non-TCP ports are skipped (UDP is not supported by kportal's forward layer).
|
2. Services: same keys. Services already in the config are locked. Non-TCP ports are skipped.
|
||||||
3. **Port assignment** — choose a starting local port (default `10000`, must be ≥ `1024`). Local ports are assigned consecutively in stable order, skipping any already in use.
|
3. Port assignment: choose a first local port (default `10000`, minimum `1024`). Ports are assigned in order and skip ports in use.
|
||||||
|
|
||||||
Press `enter` on the final step to save (or to print and exit when `--dry-run` is set), `b` to go back, or `esc` to cancel.
|
Press `enter` on the last step to save (or print, with `--dry-run`), `b` to go back, `esc` to cancel.
|
||||||
|
|
||||||
## Status Indicators
|
The `--dry-run` output has this form (example values):
|
||||||
|
|
||||||
| Indicator | Description |
|
```text
|
||||||
|-----------|-------------|
|
[dry-run] Would add 2 forwards to .kportal.yaml
|
||||||
|
10000 → my-cluster/default/service/api:8080
|
||||||
|
10001 → my-cluster/default/service/postgres:5432
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shell completion
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kportal completion --shell zsh # print the script
|
||||||
|
kportal completion --shell zsh --install # install it
|
||||||
|
kportal completion --uninstall # remove it
|
||||||
|
```
|
||||||
|
|
||||||
|
Without `--shell`, kportal detects the shell. Supported shells: bash, zsh, fish.
|
||||||
|
|
||||||
|
## Status indicators
|
||||||
|
|
||||||
|
| Indicator | Meaning |
|
||||||
|
|-----------|---------|
|
||||||
| `● Active` | Connection healthy |
|
| `● Active` | Connection healthy |
|
||||||
| `○ Starting` | Initial connection (10s grace period) |
|
| `○ Starting` | First connection. Failed health checks show Starting for the first 10s. |
|
||||||
| `◐ Reconnecting` | Reconnecting after failure |
|
| `◐ Reconnecting` | Reconnecting after a failure |
|
||||||
| `✗ Error` | Connection failed |
|
| `✗ Error` | Connection failed |
|
||||||
| `○ Disabled` | Manually disabled |
|
| `○ Disabled` | Turned off in the UI |
|
||||||
|
|
||||||
## Advanced Features
|
## Advanced features
|
||||||
|
|
||||||
### HTTP Traffic Logging
|
### HTTP traffic logging
|
||||||
|
|
||||||
Press `l` in the TUI to view real-time HTTP traffic for a selected forward. The log viewer shows:
|
Press `l` on a forward that has `httpLog` set.
|
||||||
|
|
||||||
| Column | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| TIME | Request timestamp |
|
|
||||||
| METHOD | HTTP method (GET, POST, etc.) |
|
|
||||||
| STATUS | Response status code |
|
|
||||||
| LATENCY | Request duration |
|
|
||||||
| PATH | Request path |
|
|
||||||
|
|
||||||
**List view shortcuts:**
|
|
||||||
|
|
||||||
| Key | Action |
|
|
||||||
|-----|--------|
|
|
||||||
| `↑/↓` | Navigate entries |
|
|
||||||
| `Enter` | View request details |
|
|
||||||
| `g/G` | Jump to top/bottom |
|
|
||||||
| `a` | Toggle auto-scroll |
|
|
||||||
| `f` | Cycle filter mode (All → Non-2xx → Errors) |
|
|
||||||
| `/` | Search by path or method |
|
|
||||||
| `c` | Clear all filters |
|
|
||||||
| `q` | Close log viewer |
|
|
||||||
|
|
||||||
**Detail view:**
|
|
||||||
|
|
||||||
Press `Enter` on any entry to see full request/response details including:
|
|
||||||
- Request and response headers (alphabetically sorted)
|
|
||||||
- Request and response bodies
|
|
||||||
- Timing information and status codes
|
|
||||||
|
|
||||||
| Key | Action |
|
|
||||||
|-----|--------|
|
|
||||||
| `↑/↓` | Scroll content |
|
|
||||||
| `PgUp/PgDn` | Scroll by page |
|
|
||||||
| `g` | Jump to top |
|
|
||||||
| `c` | Copy response body to clipboard |
|
|
||||||
| `Esc/q` | Return to list |
|
|
||||||
|
|
||||||
**Body display features:**
|
|
||||||
- **JSON formatting** - JSON bodies are pretty-printed with syntax highlighting
|
|
||||||
- **Compression handling** - gzip/deflate content is automatically decompressed
|
|
||||||
- **Binary detection** - Binary content shows a placeholder instead of garbled data
|
|
||||||
|
|
||||||
**Filter modes:**
|
|
||||||
- **All** - Show all entries
|
|
||||||
- **Non-2xx** - Hide successful (2xx) responses
|
|
||||||
- **Errors** - Show only 4xx and 5xx responses
|
|
||||||
|
|
||||||
**Toggling per-forward logging:**
|
|
||||||
|
|
||||||
In the add/edit wizard, press `h` on the confirmation step to toggle `httpLog` on or
|
|
||||||
off for the current forward. The wizard preserves any advanced `httpLog` keys
|
|
||||||
(`logFile`, `includeHeaders`, `maxBodySize`, `filterPath`) you set in YAML.
|
|
||||||
|
|
||||||
**Header redaction:**
|
|
||||||
|
|
||||||
When `httpLog.includeHeaders: true` is set, sensitive header values are
|
|
||||||
automatically replaced with `[REDACTED]`. The header name is preserved so you can
|
|
||||||
see that an `Authorization` header was present without exposing its value. Redacted
|
|
||||||
headers include `Authorization`, `Cookie`, `Set-Cookie`, `X-Api-Key`,
|
|
||||||
`X-Auth-Token`, `X-Csrf-Token`, `Proxy-Authorization`, `X-Access-Token`, and any
|
|
||||||
header whose name contains `token`, `secret`, `password`, or `apikey`. This is
|
|
||||||
always on and cannot be disabled.
|
|
||||||
|
|
||||||
**Advanced configuration:**
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
forwards:
|
forwards:
|
||||||
@@ -474,48 +483,88 @@ forwards:
|
|||||||
localPort: 8080
|
localPort: 8080
|
||||||
httpLog:
|
httpLog:
|
||||||
enabled: true
|
enabled: true
|
||||||
includeHeaders: true # values of sensitive headers are redacted
|
includeHeaders: true # sensitive values are redacted
|
||||||
maxBodySize: 65536 # bytes; 0 = unlimited
|
maxBodySize: 65536 # bytes
|
||||||
filterPath: "/api/" # only log paths matching this substring
|
filterPath: "/api/" # log only paths containing this text
|
||||||
logFile: "api.log" # append entries to a file in addition to the in-memory ring
|
logFile: "api.log" # also append entries to this file
|
||||||
```
|
```
|
||||||
|
|
||||||
### Connection Benchmarking
|
`maxBodySize` defaults to 1 MB (1048576 bytes) when you omit it or set a value of 0 or less.
|
||||||
|
|
||||||
Press `b` in the TUI to benchmark a selected forward. Configure:
|
List view columns: TIME, METHOD, STATUS, LATENCY, PATH.
|
||||||
|
|
||||||
- **URL Path** - Target endpoint (default: `/`)
|
| Key | Action |
|
||||||
- **Method** - HTTP method (GET, POST, etc.)
|
|-----|--------|
|
||||||
- **Concurrency** - Number of parallel workers
|
| `Up` `Down` | Move |
|
||||||
- **Requests** - Total number of requests
|
| `Enter` | Open details |
|
||||||
|
| `g` / `G` | Top / bottom |
|
||||||
|
| `a` | Toggle auto-scroll |
|
||||||
|
| `f` | Cycle filter: All, Non-2xx, Errors |
|
||||||
|
| `/` | Search path or method |
|
||||||
|
| `c` | Clear filters |
|
||||||
|
| `q` | Close |
|
||||||
|
|
||||||
Results include:
|
The detail view shows sorted request and response headers, bodies and timing. JSON bodies are pretty-printed, gzip and deflate bodies are decoded, and binary bodies show a placeholder.
|
||||||
- Success/failure counts
|
|
||||||
- Min/Max/Avg latency
|
|
||||||
- P50/P95/P99 percentiles
|
|
||||||
- Throughput (requests/sec)
|
|
||||||
- Status code distribution
|
|
||||||
|
|
||||||
### Hot-Reload
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| `Up` `Down` / `PgUp` `PgDn` | Scroll |
|
||||||
|
| `g` | Top |
|
||||||
|
| `c` | Copy the response body |
|
||||||
|
| `Esc` / `q` / `Enter` | Back to the list |
|
||||||
|
|
||||||
Configuration changes are applied automatically. Manual reload:
|
#### Sensitive header redaction
|
||||||
|
|
||||||
|
With `includeHeaders: true`, kportal replaces these header values with `[REDACTED]` and keeps the name: `Authorization`, `Cookie`, `Set-Cookie`, `X-Api-Key`, `X-Auth-Token`, `X-Csrf-Token`, `Proxy-Authorization`, `X-Access-Token`, and any name containing `token`, `secret`, `password` or `apikey` (any case). You cannot turn this off. Source: `internal/httplog/proxy.go`.
|
||||||
|
|
||||||
|
### Benchmarking
|
||||||
|
|
||||||
|
Press `b` on a forward. Set the URL path (default `/`), the HTTP method, the concurrency and the request count. The wizard starts at 10 workers and 100 requests.
|
||||||
|
|
||||||
|
The results screen has this layout (`x.xx` marks a measured value):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Total Requests: 100
|
||||||
|
Successful: 100 (100.0%)
|
||||||
|
Failed: 0
|
||||||
|
|
||||||
|
Latency (ms)
|
||||||
|
Min: x.xx
|
||||||
|
Max: x.xx
|
||||||
|
Avg: x.xx
|
||||||
|
P50: x.xx
|
||||||
|
P95: x.xx
|
||||||
|
P99: x.xx
|
||||||
|
|
||||||
|
Throughput
|
||||||
|
Requests/sec: x.xx
|
||||||
|
Bytes read: n
|
||||||
|
|
||||||
|
Status Codes
|
||||||
|
200: n
|
||||||
|
```
|
||||||
|
|
||||||
|
Only 2xx responses count as successful.
|
||||||
|
|
||||||
|
### Hot reload
|
||||||
|
|
||||||
|
kportal reloads when the config file changes. To reload by hand:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kill -HUP $(pgrep kportal)
|
kill -HUP $(pgrep kportal)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Port Conflict Detection
|
New forwards start, removed forwards stop and unchanged forwards keep running. An invalid config is rejected and the previous one stays active.
|
||||||
|
|
||||||
kportal validates port availability at startup and during hot-reload, showing which process is using conflicting ports.
|
### Port conflict detection
|
||||||
|
|
||||||
Conflicts are only reported between forwards that could be running together.
|
kportal checks that local ports are free at startup and on reload. The report names the process that holds a busy port. kportal finds it with `lsof` on Linux and macOS and `tasklist` on Windows. A reload that would bind a busy port is rejected and the previous config stays active.
|
||||||
With `--context`, forwards in contexts outside the selection are ignored, which
|
|
||||||
is what lets each context reuse the same `localPort` values - see
|
|
||||||
[Reusing local ports across contexts](#reusing-local-ports-across-contexts).
|
|
||||||
|
|
||||||
### Retry Strategy
|
Conflicts are only reported between forwards that can run together. See [Select contexts](#select-contexts).
|
||||||
|
|
||||||
Exponential backoff: 1s → 2s → 4s → 8s → 10s (max). Retries continue indefinitely until connection succeeds.
|
### Retry strategy
|
||||||
|
|
||||||
|
Exponential backoff with 10% jitter: 1s, 2s, 4s, 8s, then 10s (cap). Retries continue until the connection succeeds.
|
||||||
|
|
||||||
## Migration from kftray
|
## Migration from kftray
|
||||||
|
|
||||||
@@ -523,88 +572,63 @@ Exponential backoff: 1s → 2s → 4s → 8s → 10s (max). Retries continue ind
|
|||||||
kportal --convert configs.json --convert-output .kportal.yaml
|
kportal --convert configs.json --convert-output .kportal.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Signal Handling
|
`-convert-output` defaults to `.kportal.yaml`.
|
||||||
|
|
||||||
- `Ctrl+C` / `SIGTERM` - Graceful shutdown
|
## Signal handling
|
||||||
- `SIGHUP` - Reload configuration
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
| Signal | Effect |
|
||||||
|
|--------|--------|
|
||||||
|
| `Ctrl+C`, `SIGTERM` | Graceful shutdown |
|
||||||
|
| `SIGHUP` | Reload the configuration |
|
||||||
|
|
||||||
### Port Already in Use
|
## Troubleshooting
|
||||||
|
|
||||||
|
Port already in use:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lsof -i :<port>
|
lsof -i :<port>
|
||||||
kill <pid>
|
kill <pid>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Connection Refused
|
Connection refused:
|
||||||
|
|
||||||
1. Verify pod is running: `kubectl get pods -n <namespace>`
|
1. Check the pod: `kubectl get pods -n <namespace>`
|
||||||
2. Verify port is correct: `kubectl describe pod <pod>`
|
2. Check the port: `kubectl describe pod <pod>`
|
||||||
3. Check service endpoints: `kubectl get endpoints <service>`
|
3. Check the service endpoints: `kubectl get endpoints <service>`
|
||||||
|
|
||||||
### Context Not Found
|
Context not found: list your contexts with `kubectl config get-contexts`. kportal reports `context <name> not found in kubeconfig` when a `contexts[].name` value is not in your kubeconfig. Names with `@`, `.`, `:` or `/` (for example `admin@home` or an EKS ARN) are valid. If `--context` reports `unknown context`, the name is missing from `contexts[].name` in the config file.
|
||||||
|
|
||||||
|
The add wizard shows a namespace prompt instead of a list: the cluster refused or returned an empty namespace list. Type the namespace name.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Requires Go 1.26 or later, kubectl and access to a cluster.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl config get-contexts
|
make build # build the binary
|
||||||
```
|
make test # run tests
|
||||||
|
|
||||||
Context names containing `@`, `.`, `:`, or `/` (e.g. `admin@home`,
|
|
||||||
`[email protected]`, GKE dotted names, EKS ARNs) are accepted by the
|
|
||||||
config validator.
|
|
||||||
|
|
||||||
If `--context` reports `unknown context "..."`, the name does not match any
|
|
||||||
`contexts[].name` in the config file. The error lists the names that are
|
|
||||||
defined - note these are the contexts in your `.kportal.yaml`, which need not be
|
|
||||||
every context in your kubeconfig.
|
|
||||||
|
|
||||||
## 🔧 Development
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- Go 1.23+
|
|
||||||
- Kubernetes cluster access
|
|
||||||
- kubectl configured
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make build # Build binary
|
|
||||||
make test # Run tests
|
|
||||||
make all # fmt, vet, staticcheck, test
|
make all # fmt, vet, staticcheck, test
|
||||||
make install # Install locally
|
make install # install locally
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Release history is in [CHANGELOG.md](CHANGELOG.md).
|
||||||
|
|
||||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
||||||
|
|
||||||
## Telemetry
|
## Telemetry
|
||||||
|
|
||||||
On startup this binary sends a single anonymous adoption ping — project name,
|
On startup kportal sends one anonymous adoption ping: project name, version and timestamp. It has a 2-second timeout and cannot block startup.
|
||||||
version, timestamp; no identifiers, no command output, no payload contents.
|
|
||||||
Fire-and-forget with a 2-second timeout; cannot block startup or panic.
|
|
||||||
|
|
||||||
See **[oss-telemetry — Disabling telemetry](https://github.com/lukaszraczylo/oss-telemetry#disabling-telemetry)**
|
To opt out, set `DO_NOT_TRACK=1`, `OSS_TELEMETRY_DISABLED=1` or `KPORTAL_DISABLE_TELEMETRY=1`. See [oss-telemetry](https://github.com/lukaszraczylo/oss-telemetry#disabling-telemetry) for the wire format and source.
|
||||||
for the exact wire format, source, and full opt-out documentation.
|
|
||||||
|
|
||||||
Quick opt-out: set any of `DO_NOT_TRACK=1`, `OSS_TELEMETRY_DISABLED=1`,
|
|
||||||
or `KPORTAL_DISABLE_TELEMETRY=1`.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE).
|
MIT. See [LICENSE](LICENSE).
|
||||||
|
|
||||||
## Acknowledgments
|
## Credits
|
||||||
|
|
||||||
- [Bubble Tea](https://github.com/charmbracelet/bubbletea) - Terminal UI framework
|
[Bubble Tea](https://github.com/charmbracelet/bubbletea), [Lipgloss](https://github.com/charmbracelet/lipgloss), [client-go](https://github.com/kubernetes/client-go). [kftray](https://github.com/hcavarsan/kftray) was the inspiration.
|
||||||
- [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling
|
|
||||||
- [client-go](https://github.com/kubernetes/client-go) - Kubernetes client
|
|
||||||
- [kftray](https://github.com/hcavarsan/kftray) - Inspiration
|
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- [Website](https://lukaszraczylo.github.io/kportal)
|
- [Website](https://lukaszraczylo.github.io/kportal)
|
||||||
- [Issues](https://github.com/lukaszraczylo/kportal/issues)
|
- [Issues](https://github.com/lukaszraczylo/kportal/issues)
|
||||||
- [Releases](https://github.com/lukaszraczylo/kportal/releases)
|
- [Releases](https://github.com/lukaszraczylo/kportal/releases)
|
||||||
- [Changelog](CHANGELOG.md)
|
|
||||||
|
|||||||
+1164
-1060
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user