{"id":49183511,"url":"https://github.com/nkuhn-vmw/cfctx","last_synced_at":"2026-04-23T03:00:48.780Z","repository":{"id":352767144,"uuid":"1216540594","full_name":"nkuhn-vmw/cfctx","owner":"nkuhn-vmw","description":"Per-shell Cloud Foundry / Tanzu context switcher — kubectx, for CF / Ops Manager / BOSH / CredHub.","archived":false,"fork":false,"pushed_at":"2026-04-21T04:19:47.000Z","size":81,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-21T04:33:24.311Z","etag":null,"topics":["bosh","cf-cli","cloud-foundry","credhub","devops","kubectx-like","ops-manager","shell","tanzu"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nkuhn-vmw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-21T02:22:25.000Z","updated_at":"2026-04-21T04:19:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"dc6186c6-1222-4364-bd97-5ac8a9290b75","html_url":"https://github.com/nkuhn-vmw/cfctx","commit_stats":null,"previous_names":["nkuhn-vmw/cfctx"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nkuhn-vmw/cfctx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nkuhn-vmw%2Fcfctx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nkuhn-vmw%2Fcfctx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nkuhn-vmw%2Fcfctx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nkuhn-vmw%2Fcfctx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nkuhn-vmw","download_url":"https://codeload.github.com/nkuhn-vmw/cfctx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nkuhn-vmw%2Fcfctx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32163851,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T02:19:40.750Z","status":"ssl_error","status_checked_at":"2026-04-23T02:17:55.737Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bosh","cf-cli","cloud-foundry","credhub","devops","kubectx-like","ops-manager","shell","tanzu"],"created_at":"2026-04-23T03:00:25.871Z","updated_at":"2026-04-23T03:00:48.759Z","avatar_url":"https://github.com/nkuhn-vmw.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cfctx\n\n**Per-shell Cloud Foundry / Tanzu context switcher. Think [kubectx](https://github.com/ahmetb/kubectx), for CF / Ops Manager / BOSH / CredHub.**\n\nTwo muscle-memory commands:\n\n```bash\ncfctx                  # list all foundations, current one highlighted\ncfctx tdc              # target tdc: source om/bosh/credhub env + cf login, done.\ncf apps                # works\nom staged-products     # works\nbosh vms               # works\ncfctx ndc              # switch foundations — tokens cached, instant\n```\n\nEach terminal gets its own `CF_HOME` directory, so `cf target`, logins, and\nOAuth tokens never collide across shells. On first touch of a foundation,\ncfctx asks **Ops Manager** via `om` for the CF API URL, BOSH env vars, and CF\nadmin creds, writes them into the per-context env file, and logs in. Tokens\ncache in `$CF_HOME/.cf/config.json`; subsequent switches are sub-second.\n\n```\n  cdc [env]  https://api.sys.cdc.example.com\n* ndc [env]  https://api.sys.ndc.example.com         ← current (color-coded in a TTY)\n  tdc [env]  https://api.sys.tdc.example.com\n```\n\n---\n\n## Why\n\nThe CF CLI stashes *everything* — API, org, space, tokens — in a single file\nunder `$CF_HOME/.cf/config.json`. With `CF_HOME=$HOME` (the default), every\nshell on the box shares one target. Run `cf target -o prod` in one tab and\nevery other tab silently follows. That's how people `cf delete-app` the\nwrong foundation.\n\n`cfctx` sets `CF_HOME` to a per-context directory *in the current shell only*.\nSibling shells are unaffected. Tokens persist between switches, so you don't\nre-login when bouncing between foundations.\n\nLayered on top of that primitive, cfctx adds:\n\n- Per-context `context.env` file (mode 0600) with `OM_*`, `BOSH_*`, `CREDHUB_*`\n  sourced on every switch.\n- **Auto-import from `om` YAML env files** you probably already have.\n- **Auto-enrichment from Ops Manager**: `BOSH_*` from `om bosh-env`, `CF_API`\n  derived from the cf product's `system_domain`, `CF_USERNAME`/`CF_PASSWORD`\n  pulled from cf's UAA admin credentials.\n- **Auto CF login** on first switch, cached on all subsequent ones.\n- **Default `CF_ORG=system` / `CF_SPACE=system`**, overridable per-context.\n- **Prompt indicator** (`cfctx prompt zsh \u003e\u003e ~/.zshrc`) with per-context\n  colors (`cfctx color prod red`).\n- `cfctx doctor` for diagnostics.\n\n---\n\n## Install\n\n### macOS\n\n```bash\n# Tanzu CLIs (adjust tap names if your org uses a mirror):\nbrew install jq\nbrew install cloudfoundry/tap/cf-cli@8\nbrew install pivotal/tap/om\nbrew install cloudfoundry/tap/bosh-cli\nbrew install cloudfoundry/tap/credhub-cli\n\n# cfctx itself:\ngit clone https://github.com/nkuhn-vmw/cfctx.git ~/.local/share/cfctx\nbash ~/.local/share/cfctx/install.sh    # idempotently wires ~/.zshrc or ~/.bash_profile\nexec $SHELL\n```\n\n### Linux (Ubuntu / Debian)\n\n```bash\n# Core deps from apt:\nsudo apt-get update\nsudo apt-get install -y jq curl wget\n\n# cf CLI (Cloud Foundry):\nwget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add -\necho \"deb https://packages.cloudfoundry.org/debian stable main\" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list\nsudo apt-get update \u0026\u0026 sudo apt-get install -y cf8-cli\n\n# om CLI (Ops Manager) — binary release from pivotal-cf/om:\nOM_VERSION=7.14.2      # https://github.com/pivotal-cf/om/releases\nsudo curl -fsSL -o /usr/local/bin/om \"https://github.com/pivotal-cf/om/releases/download/${OM_VERSION}/om-linux-amd64-${OM_VERSION}\"\nsudo chmod +x /usr/local/bin/om\n\n# bosh CLI:\nBOSH_VERSION=7.7.1     # https://github.com/cloudfoundry/bosh-cli/releases\nsudo curl -fsSL -o /usr/local/bin/bosh \"https://s3.amazonaws.com/bosh-cli-artifacts/bosh-cli-${BOSH_VERSION}-linux-amd64\"\nsudo chmod +x /usr/local/bin/bosh\n\n# credhub CLI (optional but recommended):\nCREDHUB_VERSION=2.9.45 # https://github.com/cloudfoundry/credhub-cli/releases\ncurl -fsSL -o /tmp/credhub.tgz \"https://github.com/cloudfoundry/credhub-cli/releases/download/${CREDHUB_VERSION}/credhub-linux-amd64-${CREDHUB_VERSION}.tgz\"\nsudo tar -xzf /tmp/credhub.tgz -C /usr/local/bin/ credhub \u0026\u0026 rm /tmp/credhub.tgz\n\n# cfctx itself (same as macOS from here):\ngit clone https://github.com/nkuhn-vmw/cfctx.git ~/.local/share/cfctx\nbash ~/.local/share/cfctx/install.sh    # wires ~/.bashrc (or ~/.zshrc if you use zsh)\nexec $SHELL\n```\n\n**Linux notes:**\n- Default shell is bash, so `install.sh` appends to `~/.bashrc`. For zsh users (`chsh -s $(which zsh)`), it wires `~/.zshrc` instead.\n- `bash install.sh` is idempotent — safe to re-run. The source line is bracketed with `# \u003e\u003e\u003e cfctx \u003e\u003e\u003e` / `# \u003c\u003c\u003c cfctx \u003c\u003c\u003c` markers for clean uninstall.\n- `jq` is required for automatic `CF_API` / CF-admin-credential detection. BOSH-env enrichment works without jq.\n- For air-gapped installs, download the tarballs from a workstation and `scp` them in — cfctx itself has no runtime deps beyond the standard Tanzu CLIs.\n\n### Manual install (any platform)\n\n```bash\ngit clone https://github.com/nkuhn-vmw/cfctx.git ~/.local/share/cfctx\necho 'source ~/.local/share/cfctx/cfctx.sh' \u003e\u003e ~/.zshrc    # or ~/.bashrc\nexec $SHELL\n```\n\nIf you already keep `om` env files in a single directory, point cfctx at\nthem so it can auto-discover:\n\n```bash\necho 'export CFCTX_OM_ENV_DIR=\"$HOME/env\"' \u003e\u003e ~/.zshrc\n```\n\nDefault auto-discovery patterns (for context name `tdc`):\n`tdc.yml` → `tdc.yaml` → `om-cli-tdc.yml` → `om-cli-tdc.yaml`. Override with\n`CFCTX_OM_ENV_PATTERNS=\"foundation-NAME.yaml ...\"` (`NAME` is the placeholder).\n\nAdd the prompt indicator:\n\n```bash\ncfctx prompt zsh \u003e\u003e ~/.zshrc       # (or: cfctx prompt bash)\n```\n\n---\n\n## Daily use\n\n```bash\ncfctx                # list all foundations\ncfctx tdc            # target (idempotent — first call does full enrichment)\ncf apps              # works\nom staged-products   # works (OM_* already in shell)\nbosh vms             # works (BOSH_* already in shell)\ncfctx ndc            # switch to a different foundation; tokens cached\ncfctx                # list again; NDC now the active one\ncfctx clear          # unset CF_HOME + OM_*/BOSH_*/CREDHUB_* in this shell\n```\n\n### Change org / space\n\n```bash\ncfctx tdc --org myteam --space staging    # persist to tdc's context.env\ncf target -o otherone                      # temporary (this shell only)\n```\n\n### Color-code foundations\n\n```bash\ncfctx color lab    green\ncfctx color prod   red\ncfctx color stg    yellow\n```\n\nThe prompt segment (if you installed it) will pick up the color on next switch.\n\n### Re-enrich from Ops Manager\n\nIf a foundation gets re-paved, certs rotate, or the CF password changes,\nrefresh the env file in place:\n\n```bash\ncfctx enrich tdc\n```\n\nPreserves any manual edits outside the `BOSH-FROM-OM` sentinel block.\n\n### Diagnostic\n\n```bash\ncfctx doctor              # tools, env, per-context state\ncfctx doctor --online     # also probe each foundation's OM reachability\n```\n\n---\n\n## Subcommands\n\n| Command | What it does |\n|---|---|\n| `cfctx` | List all foundations, current highlighted, with `[env]` and target URL. |\n| `cfctx \u003cname\u003e [flags]` | Target a foundation: stamp `context.env`, enrich from OM, switch, auto-login. |\n| `cfctx target \u003cname\u003e` | Explicit alias for the bare form. |\n| `cfctx pick` | fzf-based interactive picker (requires `fzf`). |\n| `cfctx enrich [name]` | Re-query Ops Manager to refresh `BOSH_*` / `CF_API` / CF creds. Idempotent. |\n| `cfctx edit [name]` | Open `context.env` in `$EDITOR` (stamps a template if absent). |\n| `cfctx env [name]` | Print `context.env` with secret-looking values masked. |\n| `cfctx init-env \u003cname\u003e` | Stamp only (no switch, no login). `--from-om \u003cfile\u003e`, `--force`, `--no-enrich`. |\n| `cfctx cp \u003csrc\u003e \u003cdst\u003e` | Duplicate a context (including env file). |\n| `cfctx mv \u003cold\u003e \u003cnew\u003e` | Rename a context. |\n| `cfctx rm \u003cname\u003e` | Delete a context. Clears `CF_HOME` if it matched. |\n| `cfctx status` | Current-context details (CF_HOME + `cf target` output). |\n| `cfctx doctor [--online]` | Health check: tools, env, per-context state (+ OM reachability). |\n| `cfctx prompt [zsh\\|bash\\|starship]` | Emit a prompt snippet. |\n| `cfctx color \u003cname\u003e [\u003ccolor\u003e\\|clear]` | Set/clear per-context prompt color. |\n| `cfctx clear` | `unset CF_HOME` + `OM_*` / `BOSH_*` / `CREDHUB_*` in this shell. |\n| `cfctx version` / `cfctx help` | Self-explanatory. |\n\nFlags on `cfctx \u003cname\u003e` / `cfctx target \u003cname\u003e`:\n\n| Flag | Effect |\n|---|---|\n| `--from-om \u003cfile\u003e` | Pre-fill `OM_*` from a specific `om` env yaml. |\n| `--cf-api \u003curl\u003e` | Persist `CF_API` (usually auto-detected — use when OM enrichment can't run). |\n| `--org \u003cname\u003e`, `--space \u003cname\u003e` | Persist `CF_ORG` / `CF_SPACE`. |\n| `--force` | Re-seed `context.env` from scratch. |\n| `--no-login` | Skip CF auto-login for this switch. |\n| `--no-enrich` | Skip the OM API calls (yaml-only import). |\n| `--create` | Bypass the \"create new blank context?\" confirmation. |\n\n---\n\n## Interactive picker (fzf)\n\nIf you have `fzf` installed, pick a foundation interactively:\n\n```bash\ncfctx pick\n```\n\nOpens an fzf prompt over all configured contexts with a preview pane showing\n`CF_API`, `CF_ORG`, `CF_SPACE`, `OM_TARGET`, `BOSH_ENVIRONMENT` (secrets\nredacted). Type to filter, Enter to switch — runs the full `cfctx \u003cname\u003e`\nflow (auto-login, wraps, etc.). `Esc` cancels without switching.\n\nIf fzf isn't installed, `cfctx pick` prints a clear install hint and exits\nnon-zero; everything else in cfctx keeps working without fzf.\n\n## Token-expiry awareness\n\nEvery `cfctx \u003cname\u003e` silently decodes the cached CF token's JWT `exp` claim.\nIf it's within 60s of expiring (or already expired), auto-login re-authenticates\ntransparently — no more \"logged out mid-session\" surprises after a tab sits\nidle overnight. Requires `jq`; without it, tokens are trusted based on\npresence only (previous behavior).\n\n`cfctx doctor` surfaces expiry per-context:\n\n```\n  tdc\n    [✓] CF token cached (expires in 23h)\n  cdc\n    [!] CF token EXPIRED 2h ago — next switch will re-auth\n```\n\n## Ghostty / Kitty / Alacritty (`xterm-ghostty` on remote hosts)\n\nIf your local terminal sets `TERM=xterm-ghostty` (or `xterm-kitty`,\n`alacritty-direct`), remote hosts that don't have that terminfo entry\ninstalled will fail curses-based tools with:\n\n```\nError opening terminal: xterm-ghostty.\n```\n\nOn switch, cfctx detects an \"exotic\" TERM and silently installs two\nshell-function overrides:\n\n```bash\nbosh ssh director-0     # really runs: TERM=xterm-256color command bosh ssh director-0\ncf ssh my-app           # really runs: TERM=xterm-256color command cf ssh my-app\n```\n\nNo typing changes. Local-shell `$TERM` is untouched, so Ghostty's\nnative features (image protocol, cursor shapes) keep working for\nlocal commands.\n\nSafety:\n\n- Only triggers for specific exotic TERMs (falls through for `xterm-256color`, `tmux-256color`, `screen-256color`, etc.).\n- Skips installation if you already have your own `bosh` / `cf` alias or function.\n- Marker comment (`__cfctx_term_wrap__`) in the generated functions ensures uninstall only removes our own wraps, not user-defined ones.\n\nKnobs:\n\n```bash\nexport CFCTX_NO_TERM_WRAPS=1         # disable entirely\nexport CFCTX_SAFE_TERM=xterm         # use a different fallback TERM\n```\n\nUninstall is automatic on `cfctx clear`. Status is visible in `cfctx doctor`.\n\n## Security posture\n\n- **Per-context env files live at `$CFCTX_ROOT/\u003cname\u003e/context.env` (default\n  `~/.cf-homes/\u003cname\u003e/context.env`), mode `0600`.** cfctx refuses to source\n  files with looser permissions, and re-applies `0600` after every edit.\n- **Never committed to git.** The repo's `.gitignore` excludes `*.env`,\n  `context.env`, `.cf-homes/`, `.cf/`, common key filenames.\n- **`cfctx env \u003cname\u003e`** masks values for any key matching\n  `PASSWORD|SECRET|TOKEN|KEY|CA_CERT|PRIVATE` so output can be pasted safely.\n- **CF admin passwords** retrieved from Ops Manager contain special\n  characters (`$`, `\"`, `\\`, backtick) — cfctx escapes them for safe shell\n  re-sourcing. Round-trip tested.\n- **Prefer a secret manager over plaintext** when possible. `context.env`\n  is a regular shell file, so you can use:\n  ```bash\n  export OM_PASSWORD=\"$(security find-generic-password -s om-tdc -w)\"   # Keychain\n  export BOSH_CLIENT_SECRET=\"$(pass show tanzu/tdc/bosh)\"                # pass\n  export OM_PASSWORD=\"$(op read 'op://Private/om-tdc/credential')\"       # 1Password\n  ```\n  These run every time you switch, giving you auto-refresh of short-lived\n  creds.\n\n---\n\n## Why each of `om` / `bosh` / `credhub` / `cf` behaves differently\n\n| Tool | How auth persists | Requires env creds per call? |\n|---|---|---|\n| `cf` | OAuth tokens cached in `$CF_HOME/.cf/config.json` | no — tokens cached |\n| `om` | none | **yes** — every call re-auths |\n| `bosh` | short-lived refresh tokens in `~/.bosh/config` | **yes** — client/secret still needed |\n| `credhub` | none | **yes** |\n\nThat's why `OM_*`, `BOSH_*`, and `CREDHUB_*` all live in `context.env` and\nget sourced on every switch, while `CF_*` is mostly auto-populated on the\nfirst login and then cached.\n\n---\n\n## Testing\n\n```bash\nbrew install bats-core\nbats tests/\n```\n\nThe suite includes mock `cf`, `om`, and `fzf` binaries plus a JWT builder\nhelper, so it runs without network or a real foundation. **112 tests**,\ncovering: switch/ls/clear, env-file handling, om yaml import, om-enrichment\nhappy/unreachable paths, CF auto-login (fresh, cached, expired-JWT,\ncredential-failure, org/space targeting), typo guard, prompt snippet\nemission, color tagging, `cfctx doctor`, TERM wraps, JWT expiry parsing,\nand the fzf picker.\n\nCI runs six jobs on Ubuntu + macOS: `shellcheck`, `bats` × 2, `zsh-smoke`\n× 2 (sources cfctx.sh under real zsh and exercises helpers known to\ndrift between shells), and `install-smoke` (runs install.sh in a\ndisposable HOME and asserts idempotency + source-ability).\n\n---\n\n## Repository layout\n\n```\ncfctx/\n├── README.md\n├── LICENSE                           # Apache-2.0\n├── cfctx.sh                          # the sourced shell function\n├── install.sh                        # idempotent rc-file installer\n├── completions/\n│   ├── cfctx.zsh\n│   └── cfctx.bash\n├── examples/\n│   └── context.env.example           # annotated template (committed — safe)\n├── tests/\n│   ├── README.md\n│   ├── switch.bats\n│   ├── env.bats\n│   ├── target.bats\n│   ├── autologin.bats\n│   ├── enrich.bats\n│   ├── default-verb.bats\n│   ├── tier1.bats                    # prompt / color / doctor / did-you-mean\n│   ├── term-wraps.bats                # bosh/cf TERM overrides on exotic terminals\n│   ├── token-expiry.bats              # JWT exp decoding + re-auth flow\n│   └── pick.bats                      # fzf picker (uses a mock fzf)\n├── docs/\n│   ├── tanzu-integration.md\n│   └── roadmap.md                     # design doc + pending items\n└── .github/\n    └── workflows/\n        └── ci.yml\n```\n\n---\n\n## Status and roadmap\n\nv0.3.0. Production-ready for single-user workstations. Full design doc\nand roadmap in [`docs/roadmap.md`](docs/roadmap.md).\n\n**Shipped since v0.2.0:**\n\n- Ghostty/Kitty/Alacritty TERM wraps for remote `bosh ssh` / `cf ssh`.\n- Token-expiry awareness via JWT `exp` claim (silent re-auth near expiry).\n- `cfctx pick` — fzf-based interactive picker with preview pane.\n- Cross-foundation correctness: OM_* env from a previous switch no\n  longer leaks into the next foundation's enrichment.\n- zsh compatibility hardening (NO_MATCH glob error, `${=var}` word split).\n- BSD-vs-GNU `stat` portability across every call site.\n- Linux install docs (apt + binary-download recipes).\n- CI hardening: zsh-smoke + install-smoke jobs alongside bats on\n  ubuntu-latest + macos-latest. 112 tests total.\n\n**Pending:**\n\n- `cfctx lock \u003cname\u003e` — read-only safety rail (prompts on destructive ops).\n- direnv bridge (`cfctx direnv`) — auto-switch foundations on `cd`.\n- Homebrew tap / formula.\n- Install.sh `--link` flag (symlink instead of copy — keeps installed\n  version in sync with a local clone).\n- Windows PowerShell port (deferred).\n\n---\n\n## License\n\nApache-2.0. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnkuhn-vmw%2Fcfctx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnkuhn-vmw%2Fcfctx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnkuhn-vmw%2Fcfctx/lists"}