# Self-host Code

Deployment prerequisites, verification, and recovery for Code.

Source: https://railgrid.ai/docs/self-hosting/providers/code/


Install one Code provider into a Kubernetes cluster you administer, then enable it in a test workspace. Code manages repositories, deploy keys, and collaborators on GitHub, and keeps one kind of state outside kcp: commit bundles and git snapshots in transit between the portal and its controller.

## Prerequisites

- An existing hub and a Code provider kubeconfig from the [common provider flow](https://railgrid.ai/docs/self-hosting/providers/), saved as `code.kubeconfig`.
- A hosting cluster context, Helm, and kubectl.
- Bundle storage: a PVC-capable StorageClass for a single replica, or an S3-compatible bucket for more than one.
- Network routes from the provider to the hub, the kcp endpoints in its kubeconfig, and `github.com`.
- Optional: a GitHub OAuth App for "Connect with GitHub" in the portal.

## Prepare credentials and values

```bash
kubectl --context HOSTING-CONTEXT create namespace railgrid-provider-code
kubectl --context HOSTING-CONTEXT --namespace railgrid-provider-code \
  create secret generic railgrid-provider-kubeconfig \
  --from-file=kubeconfig=./code.kubeconfig
```

Create `code-values.yaml`. This is the single-replica form with a PVC and personal-access-token connections only:

```yaml
replicaCount: 1
hub:
  url: https://railgrid-hub.railgrid-system.svc.cluster.local:9443
  tokenSecretRef:
    name: ""
catalogEntry:
  enabled: true
bundleStore:
  backend: file
  persistence:
    enabled: true
    size: 1Gi
githubOAuth:
  enabled: false
```

Do not switch to `bundleStore.emptyDir` to hide a failed persistent-volume setup; in-flight commits are lost on every pod replacement.

### More than one replica: S3 bundle storage

The replica that accepts an upload and the leader replica that consumes it must read the same bytes, so `replicaCount > 1` requires `bundleStore.backend: s3` (the chart refuses to render otherwise). Any S3-compatible bucket works: AWS S3, MinIO, or a cloud object store through its S3 API. Railgrid's production runs two replicas against a Google Cloud Storage bucket:

```yaml
replicaCount: 2
bundleStore:
  backend: s3
  s3:
    endpoint: storage.googleapis.com
    bucket: my-code-bundles
    # GCS's S3 API takes "auto"; a real region makes the client look the
    # bucket location up, which needs more than object permissions.
    region: auto
    useSSL: true
    credentialsSecretRef:
      name: code-s3          # keys accessKeyId, secretAccessKey
```

Two things the bucket needs that are easy to miss:

- **`storage.buckets.get` for the credential.** The client checks that the bucket exists at start (`HEAD bucket`). On GCS, an HMAC key whose service account has only `roles/storage.objectAdmin` fails that check; add `roles/storage.legacyBucketReader` on the bucket.
- **A lifecycle rule.** With S3 the provider deletes a bundle once consumed but does not sweep what a crash left behind. Expire the `commit-bundles/` and `git-snapshots/` prefixes (plus `bundleStore.s3.prefix` if set) after one day.

Provision the bucket outside the chart; `createBucket` is for development loops.

### Optional: GitHub OAuth

With `githubOAuth.enabled`, the portal shows "Connect with GitHub" and mints tokens with the scopes the provider needs. Without it, users paste a personal access token, which drives repositories, deploy keys, and collaborators just as well.

Create a GitHub OAuth App **separate from the one Dex uses**: an OAuth App has exactly one callback URL. Set its Authorization callback URL to the hub's `/services` proxy path, which forwards to the provider without extra ingress:

```
https://console.example.com/services/providers/code/oauth/github/callback
```

```bash
kubectl --context HOSTING-CONTEXT --namespace railgrid-provider-code \
  create secret generic code-github-oauth --from-literal=clientSecret="<client-secret>"
```

```yaml
githubOAuth:
  enabled: true
  clientId: <client-id>
  clientSecretRef:
    name: code-github-oauth
    key: clientSecret
  redirectURL: https://console.example.com/services/providers/code/oauth/github/callback
  portalOrigin: https://console.example.com     # set it; empty broadcasts the token to "*"
```

A 503 from the OAuth round trip right after install is the provider being [unready until a workspace binds it](https://railgrid.ai/docs/self-hosting/providers/#a-fresh-provider-is-unready-until-a-workspace-binds-it), not a bad client ID.

## Render and install

```bash
helm template code oci://ghcr.io/railgrid/charts/railgrid-code-provider --version CHART_VERSION \
  --namespace railgrid-provider-code --values code-values.yaml > /tmp/code-rendered.yaml
helm upgrade --install code oci://ghcr.io/railgrid/charts/railgrid-code-provider --version CHART_VERSION \
  --kube-context HOSTING-CONTEXT --namespace railgrid-provider-code \
  --values code-values.yaml
kubectl --context HOSTING-CONTEXT --namespace railgrid-provider-code \
  get pods,pvc -l app.kubernetes.io/instance=code
```

Review the rendered resources before installing. The chart initializes the API schema and applies its CatalogEntry through the provider kubeconfig into kcp; the CatalogEntry is not a hosting-cluster resource.

## Verify the full connection

1. Check that the pod is running and, with `backend: file`, the bundle PVC is bound.
2. Check registration and heartbeat in the hub. A healthy pod alone does not verify hub-to-provider routing.
3. [Enable Code](https://railgrid.ai/docs/use/workspaces/enable-provider/) in a disposable workspace.
4. Complete the [Code quickstart](https://railgrid.ai/docs/use/code/quickstart/) with a test repository and confirm that its status is readable using the same workspace identity.

## Diagnose an installation failure

| Symptom | Check | Fix |
| --- | --- | --- |
| Init container fails with `x509` | Provider kubeconfig CA and hub trust | [Refresh the kubeconfig](https://railgrid.ai/docs/self-hosting/providers/#refresh-a-provider-kubeconfig) after fixing the hub's provider CA |
| Pod ready, `x509` against a kcp shard hostname in the logs | The hub's provider CA bundle lacks the kcp server CA | Fix the bundle, refresh the kubeconfig |
| Crash at start with a bucket error | `HEAD bucket` denied, or endpoint/region wrong | Grant `storage.buckets.get`; use `region: auto` on GCS |
| PVC stays Pending | StorageClass and hosting-cluster events | Provision compatible persistent storage before retrying |
| ImagePullBackOff | Image tag, registry access, pod events | Pin a published image version |
| CreateContainerConfigError | Referenced Secrets, including `hub.tokenSecretRef` | Create the intended Secret or remove an unused optional reference |
| "Connect with GitHub" fails | Callback URL, `portalOrigin`, provider readiness | Match the OAuth App's callback to `redirectURL` exactly; enable the provider in a workspace |

```bash
kubectl --context HOSTING-CONTEXT --namespace railgrid-provider-code get events --sort-by=.lastTimestamp
kubectl --context HOSTING-CONTEXT --namespace railgrid-provider-code \
  logs -l app.kubernetes.io/instance=code --all-containers=true --tail=100
```

Redact credentials and private repository details before sharing diagnostics.

## Remove the test installation

Remove test resources and workspace bindings before retiring the provider registration. Review upstream repository deletion policies first. Then uninstall the release:

```bash
helm uninstall code --kube-context HOSTING-CONTEXT --namespace railgrid-provider-code
```

Inspect retained PVCs and credential Secrets separately. Helm uninstall is not a repository backup. Follow [operations and recovery](https://railgrid.ai/docs/self-hosting/hub/operations/) for an existing shared provider.

[Chart values](https://github.com/railgrid/railgrid/blob/main/providers/code/deploy/chart/values.yaml) and [chart README](https://github.com/railgrid/railgrid/blob/main/providers/code/deploy/chart/README.md) are authoritative for the deployed version.
