The earlier rewrite dropped content the page needs. The landing page
has a hero with an install command, a terminal mock, the real
screenshot, feature rows with snippets from the repository, a dated
comparison with kubectl, k9s, kubefwd and kftray with source links,
install tabs, config and key references, troubleshooting and an FAQ.
The README restores the comparison, advanced features, retry strategy,
kftray migration, signal handling, signature verification and build
from source. Cells that a project does not document show as Not
documented.
It fixes four claims the code contradicts: the maxBodySize default, the
deployment resource type, the file watcher delay and the verbose table
status markers.
* docs(site): rewrite the product page in plain language
The page lists what kportal does with the flags, keys and config keys
that exist in the code. It drops the CDN assets, gradients and icon
grids, and keeps the light and dark themes.
It now covers tui.columns, typed namespace entry, completion, the log
format flag and the --context flag. The claim about a 100ms file watcher
is removed because the code has no such debounce.
* docs: rewrite the README and update the feature list
The README now has a feature table, install steps, a flag table, a
config reference and troubleshooting. It documents tui.columns, typed
namespace entry, shell completion and the --context flag.
It drops the comparison table with other tools, which the repository
cannot back up, and corrects the supported resource types and the Go
version.
* feat: add --context to forward only selected contexts
kportal forwards every context in the config file. When the file describes
several clusters that host the same services, that means holding connections
open to clusters nobody is using, and - because all contexts are active at once
- every localPort has to be globally unique. The same service therefore lands on
a different local port per cluster, so switching clusters means reconfiguring
the app being developed.
--context limits a run to the contexts you name. It is repeatable and accepts
comma-separated values, and composes with -headless:
kportal --context development-team-a
kportal --context team-a,team-b -headless
kportal --context team-a --context team-b
An unknown name is an error listing the contexts the file defines, so a typo
fails loudly instead of silently forwarding nothing.
Local-port and mDNS-hostname conflicts are now scoped to forwards that could run
at the same time: same context, or both contexts in the active selection. With
no selection every context is active, so existing behaviour is unchanged.
Contexts that are never selected together may reuse the same localPort, which is
what lets a service keep one fixed local port in every cluster.
Validation still covers the whole file, so a problem in a context outside the
selection is still reported and --check does not get weaker. Loading, validating
and filtering are shared by every path that re-reads the config (startup, file
watcher, SIGHUP in both headless and verbose modes), so no path can forget the
selection.
Also fixes a latent identity bug this makes reachable: Forward.ID() is only
"alias:localPort" when an alias is set, which was unique across contexts only
because duplicate local ports were globally rejected. With ports now shareable,
deleting or editing a forward from the TUI could have matched same-alias
forwards in other contexts and silently removed them. Delete and edit are now
scoped to the context the row came from.
* fix(config): keep the running config when no selected context remains
A reload dropped selected contexts that no longer exist in the file.
When all of them vanished, the reload returned an empty config and the
manager stopped every forward after a single rename.
LoadForRuntime now returns an error in that case. The watcher keeps the
previous config, as it does for any other invalid reload.
Refs #80
* fix(ui): apply new table columns after a successful SIGHUP reload
The context selection moved the SIGHUP reload into reloadFromSignal,
which had no way to update the verbose table columns. The function now
takes a callback that runs only after the manager reloads without error.
The verbose table passes SetColumns.
Refs #80
* style: gofmt run_test.go
---------
Co-authored-by: André Cruz <[email protected]>
* Allow configuring forwards-table columns, order, and width
Adds an optional tui.columns section to .kportal.yaml so the visible
columns, their order, and their widths can be customized instead of
always rendering the hardcoded 8-column layout. Omitting the section
keeps the existing default columns/order/widths.
Closes#81
* fix(ui): keep the plain table layout and check column widths
The plain verbose table must look the same when no tui section is
set. It had narrower columns and shorter port headers than before. The
default layout is now identical to the old one, and a golden test
guards it.
A status column that is not last now pads by visible width. Table
rules follow the column widths. The UI applies new columns only after a
reload succeeds, including the SIGHUP path of the verbose table.
The validator rejects a column width above 200. The README describes
the tui.columns setting.
Refs #81
* fix(httplog): replace the deprecated ReverseProxy Director with Rewrite
Staticcheck reports SA1019 because Go 1.26 deprecates Director. The
proxy now sets the target in a Rewrite function. Rewrite drops the
X-Forwarded-For header that Director sent, so the function calls
SetXForwarded to keep that header.
A new test checks the target port, the path and the header.
* fix(release): drop the deprecated cask url verified key
Homebrew warns about the verified parameter of the cask url stanza.
The setting is not needed. GoReleaser check now passes without a
deprecation warning.
The postflight warning remains. GoReleaser generates it from the
post install hook, and the upstream fix is not released yet.
Refs #79
---------
Co-authored-by: Lukasz Raczylo <[email protected]>
Discloses the single anonymous adoption ping sent on startup and points
users to the upstream README section for full opt-out instructions
instead of duplicating the table here.
New subcommand that connects to a chosen kube context and walks the
user through three picker steps before writing forwards to the config:
1. Namespace multi-select (no system-namespace exclusion)
2. Service multi-select grouped by namespace, with already-configured
rows greyed out and locked off
3. Starting-port input with a live preview of consecutive local-port
assignments, skipping any port already taken by the existing config
or another row in the batch
Multi-port services emit one forward per port. Non-TCP ports are
skipped with a one-line warning at the end (kportal forward layer is
TCP-only). --dry-run prints the planned forwards without writing.
Subcommand dispatch lives in cmd/kportal/main.go: when os.Args[1] ==
'generate', runGenerate(os.Args[2:]) is invoked before the main
flag.Parse() so the flag.NewFlagSet 'generate' parses its own
'--context', '--config', '--dry-run' flags. Invalid contexts list
the available ones for quick correction.
Tests in internal/ui/generate_test.go cover:
- namespace toggle / toggle-all / filter
- service multi-select with locked already-configured rows and
non-TCP filtering
- port-collision-aware consecutive assignment using a real Mutator
against a tempdir config
- reject + recover for starting-port < 1024
- dry-run does not invoke the mutator
- end-to-end Update() walk through the three steps
- parse-starting-port boundary table
- port-step view rendering
- ServiceCandidate.Key() determinism
README updated with a 'Generate Forwards from a Cluster' section
describing the flow and the three flags.
Releases are signed by the lukaszraczylo/shared-actions reusable
workflow, so the Sigstore certificate subject is the workflow URL
rather than this repo. The previous regex
'https://github.com/lukaszraczylo/kportal/.*' never matched, so any
user with cosign installed would see verification fail and abort
the install.
Pin cert-identity to the exact workflow URL:
^https://github\.com/lukaszraczylo/shared-actions/\.github/workflows/go-release\.yaml@refs/heads/main$
Override via COSIGN_CERT_IDENTITY_REGEXP for forks of the release
pipeline. Same fix applied to README's manual verification example.
Verified end-to-end against release v0.2.90:
cosign verify-blob ... -> Verified OK
P0 #8 — install.sh fetched and installed the binary with no integrity
check whatsoever, despite README claiming cosign verification. A
compromised release or registry MITM resulted in RCE on every
installer.
Now:
- downloads checksums.txt alongside the archive (required; abort on
missing)
- computes local SHA-256 with shasum -a 256 (works on macOS+Linux,
not GNU-only sha256sum)
- aborts on mismatch with a clear error
- if cosign is in PATH AND the sigstore bundle is present (the latter
already published by goreleaser), verifies cert-identity. Skipped
silently when cosign is absent so the install path still works for
users without cosign installed.
- SKIP_COSIGN=1 lets users opt out of cosign verification only
(checksum verification is always enforced).
- DRY_RUN=1 verifies + downloads but does not install, for testing.
Also replaced GNU-only `grep -oP` (silently fails on macOS BSD grep)
with portable awk for parsing kportal --version.
NOTE: the cosign cert-identity regex matches lukaszraczylo/kportal/.*
but actual releases are signed from the shared-actions reusable
workflow. Users with cosign installed will currently see a verification
failure on real releases. Either widen the regex to lukaszraczylo/.*
or change the signing identity scheme — flagging for follow-up.
README install section updated to mention the new verification.