Authenticate private repos with a GitHub App
When the agent reads scopes and actions from a private GitHub repository, the Helm chart can use a GitHub App to authenticate instead of embedding a personal access token in AGENT_REPO. This is the recommended pattern for production.
When to use this
The basic Helm install authenticates the agent repo via a token embedded in the URL:
AGENT_REPO="https://<git-token>@github.com/your-org/private-repo.git#main"
That works, but personal access tokens are long-lived, tied to a user account, and easy to leak via Helm history or kubectl describe pod. The GitHub App flow avoids both problems:
- Short-lived credentials. Each pod start generates a fresh installation token (valid for one hour).
- No personal account dependency. The App belongs to your GitHub organization, not to an individual.
- Fine-grained permissions. You grant the App access only to the specific repos the agent needs.
How it works
When the chart is deployed with githubTokenInit.enabled=true, the agent pod runs an init container before the agent itself starts. The init container reads GitHub App credentials from a Kubernetes Secret, exchanges them for a short-lived token, and clones the repo into a shared volume that the agent container then reads.
🔒 The GitHub App private key never leaves the cluster. The init container generates the JWT locally and only the resulting installation token reaches GitHub.
Prerequisites
- A Kubernetes cluster and Helm 3+ (same as the base Helm install).
- A nullplatform API key with the agent and ops roles. See Authenticate the agent.
- A GitHub App installed on the organization that owns the agent repo, with read access to the repos you want to clone.
1. Set up the GitHub App
In your GitHub organization, create a new GitHub App with the following:
- Repository permissions:
Contents: Read-only,Metadata: Read-only. - A new private key (download the
.pemfile when GitHub shows it; you can't retrieve it later).
Then install the App on your organization and select the agent repo. After installation, note down:
- The App ID (visible on the App's settings page).
- The Installation ID (the numeric ID at the end of the URL after you install the App).
- The contents of the private key
.pemfile.
2. Provide the credentials to the chart
The chart supports two patterns. Pick the one that fits your secrets workflow.
Option A: Chart-managed secret
The chart creates the Kubernetes Secret for you from values you pass via --set. This is the simplest option for development or single-team installs.
The private key value must be base64-encoded. Generate it from your .pem file:
export PRIVATE_KEY_B64=$(base64 -w 0 < path/to/private-key.pem)
Then pass all three credentials when you install the chart:
helm install nullplatform-agent nullplatform/nullplatform-agent \
--namespace nullplatform-tools --create-namespace \
--set githubTokenInit.enabled=true \
--set githubTokenInit.repositoryUrl=github.com/your-org/private-repo.git \
--set githubTokenInit.values.appId=<your_app_id> \
--set githubTokenInit.values.installationId=<your_installation_id> \
--set githubTokenInit.values.privateKey=$PRIVATE_KEY_B64 \
--set configuration.values.NP_API_KEY=$NP_API_KEY \
--set configuration.values.TAGS="$AGENT_TAGS"
The chart creates a Secret named github-app-secret and wires the init container to read from it.
Option B: Bring your own secret
For production or GitOps workflows, you typically manage Secrets through a tool like External Secrets Operator or sealed-secrets. In that case, pre-create the Secret yourself and point the chart at it.
Create a Secret with these three keys (default names: APP_ID, INSTALLATION_ID, PRIVATE_KEY):
apiVersion: v1
kind: Secret
metadata:
name: github-app-secret
namespace: nullplatform-tools
type: Opaque
stringData:
APP_ID: "<your_app_id>"
INSTALLATION_ID: "<your_installation_id>"
PRIVATE_KEY: "<base64-encoded private key PEM>"
Then install the chart referencing the existing Secret:
helm install nullplatform-agent nullplatform/nullplatform-agent \
--namespace nullplatform-tools --create-namespace \
--set githubTokenInit.enabled=true \
--set githubTokenInit.repositoryUrl=github.com/your-org/private-repo.git \
--set githubTokenInit.secretName=github-app-secret \
--set configuration.values.NP_API_KEY=$NP_API_KEY \
--set configuration.values.TAGS="$AGENT_TAGS"
Override githubTokenInit.secretKeys.* if you use different key names in your Secret.
3. Verify the init container ran
Once the chart is installed, check the init container logs to confirm the clone succeeded:
kubectl logs -n nullplatform-tools <agent-pod-name> -c github-token-init
You should see the token request to GitHub and the repo clone output. If the agent pod is Running, the agent container is connected and the cloned repo is mounted at /root/.np/<repo-name>.
Then verify the agent appears in the platform UI under Platform settings > Agents.
Configuration reference
All values live under githubTokenInit in the chart values.
| Value | Default | What it does |
|---|---|---|
enabled | false | Adds the init container that authenticates with GitHub and clones the repo |
repositoryUrl | "" | Repo URL to clone. Omit the https:// prefix (the init container prepends it with the token). Example: github.com/your-org/private-repo.git |
secretName | github-app-secret | Name of the Kubernetes Secret holding the App credentials |
secretKeys.appId | APP_ID | Key inside the Secret for the App ID |
secretKeys.installationId | INSTALLATION_ID | Key inside the Secret for the Installation ID |
secretKeys.privateKey | PRIVATE_KEY | Key inside the Secret for the base64-encoded private key |
values.appId | "" | App ID, when letting the chart create the Secret (Option A) |
values.installationId | "" | Installation ID, when letting the chart create the Secret (Option A) |
values.privateKey | "" | Base64-encoded PEM, when letting the chart create the Secret (Option A) |
workingDir | /root/.np | Shared volume mount path. Must match the agent's command executor base path |
image | alpine:3.22.2 | Init container image. Must include apk |
Refreshing the cloned repo
The init container runs only at pod startup. To pull new commits from the agent repo after the pod is running, restart the agent so the init container runs again with a fresh token:
kubectl rollout restart deployment/nullplatform-agent-<release-name> -n nullplatform-tools
The refresh-sources command from the platform UI only works when the agent has its own clone credentials configured via AGENT_REPO. With GitHub App auth, treat pod restarts as the way to update the code the agent runs.
Next step
Once the agent is installed and registered, create a notification channel so it can respond to platform events.