---
id: w1/m19.1/t003
title: Rewrite CI cluster access — SSH-to-CP replaces SSH-to-infra (before infra dies)
worker: worker1
status: done
estimate: 20m
depends_on: [w1/m19.1/t002]
---

## Objective

Both workflows currently reach the app cluster by SSHing `bex-infra` for the mgmt kubeconfig and running `clusterctl get kubeconfig`. That path dies with `bex-infra` (t004) — replace it first, so CI never has a broken window.

## Context

- app-cluster.yml: "fetch infra kubeconfig" step (terraform output IP → SSH k3s.yaml → clusterctl get kubeconfig). Post-pivot its overlay apply must target the APP cluster (CAPI objects live there now).
- deploy.yml: "discover infra cluster IP" + "get app cluster kubeconfig" steps — same dependency.
- New path: discover a CP IP via the hcloud API (label selector or name prefix `bex-control-plane`), `ssh root@CP 'cat /etc/kubernetes/admin.conf'`, rewrite the server field to the kube-api LB endpoint. Same `bex` SSH key CI already holds; `:22` stays authentication-only (DO_NOT_DO baseline — no source-IP allowlist).
- terraform steps that only read state for the infra IP become dead — remove or gate them.

## Steps

1. Rewrite the kubeconfig-fetch steps in both workflows; drop the clusterctl-from-mgmt dependency (post-pivot, app-cluster.yml's clusterctl init/apply also targets the app cluster).
2. Trigger both via workflow_dispatch against the pivoted (infra-still-alive) cluster — green proves the new path before t004 burns the old one.

## Files

- .github/workflows/app-cluster.yml
- .github/workflows/deploy.yml

## Acceptance criteria

- [ ] Neither workflow references the infra kubeconfig/IP for cluster access.
- [ ] Both workflows green via workflow_dispatch using the SSH-to-CP path, while bex-infra still exists (rollback stays possible).

## Out of scope

- CI secret rotation (stays in m19 t008).
