Credentials and secrets
TruStacks never holds production credentials. The Runner needs a tightly scoped set of secrets to read your application repositories, talk to your LLM provider, and open pull requests against your platform repository. Everything else (cluster credentials, cloud keys, production secrets) stays in your environment and never crosses the data-plane boundary.
This page covers the four secrets the Runner needs to do useful work, how each one gets provisioned, and the trust boundary around them.
The four Runner secrets
| Secret | What it is | How it is provisioned |
|---|---|---|
| LLM provider key | Anthropic or OpenRouter API key | UI (Settings → LLM Provider) or .env + make secrets for fresh-cluster bootstrap |
| Per-repo SSH deploy keys | ed25519 keypair per connected repo | Generated by the Connect-a-repo wizard or by scripts/setup_runner_access.py in the local-dev flow |
| Git API token | Gitea token (local) or GitHub App installation token (real) | Connect-a-repo wizard (auto-provisioned) or scripts/setup_runner_access.py (local-dev) |
| Signed policy bundle | The signed constitution OCI artifact | Pushed to your Zot (local) or the TruStacks registry (production) by make policy-bundle or the standard release pipeline |
The Runner’s init container materializes each Secret at a stable path before the main container starts. The agent runtime never sees the raw Secret contents; it reads only the materialized paths.
LLM provider key
The Runner talks to Claude through one of two providers. Both providers route to Claude Sonnet 4.5+; provider choice is a billing relationship, not a model selection (see the architecture reference).
Where the Secret lives
- K8s Secret ·
trustacks-runner-llmin thetrustacks-systemnamespace. - Fields ·
ANTHROPIC_API_KEY,OPENROUTER_API_KEY,LLM_PROVIDER,OPENROUTER_MODEL. All entries areoptional: trueso the chart installs cleanly into a fresh cluster before you have saved a config.
How it gets provisioned
Two paths, depending on whether the Control Plane is up.
UI (preferred) · open http://ui.localtest.me:8080/#/settings/llm,
paste your key, confirm, save. The Control Plane runs a live ping at the
provider, rewrites the Secret, and rolls the Runner.
.env + make secrets (fresh-cluster bootstrap) · used before the
Control Plane has booted. Set ANTHROPIC_API_KEY=<key> in .env, run
make secrets. The Control Plane lifts the legacy Secret into the new
flow on first boot.
Rotating the key
UI flow: paste the new key in Settings → LLM Provider and save. The Runner pod is rolled automatically.
.env flow: update ANTHROPIC_API_KEY and run make secrets.
Per-repo SSH deploy keys
The Runner clones connected repositories over SSH using ed25519 deploy keys. Each repo gets its own keypair. The Runner discovers keys dynamically at pod startup via labeled K8s Secrets, so adding a new repository never requires a Helm-values change.
Where the Secrets live
- K8s Secret ·
<repo>-deploy-keyin thetrustacks-systemnamespace. - Labels ·
trustacks.io/role=git-deploy-key,trustacks.io/repo=<repo-name>. - Materialized path ·
/var/run/secrets/git/<repo>/.
How they get provisioned
Two paths.
Connect-a-repo wizard (Gitea path) · the UI generates an ed25519 keypair in-memory, registers the public key with Gitea, creates the labeled K8s Secret, and rolls the Runner. The private key never touches disk outside of the K8s Secret.
scripts/setup_runner_access.py (local-dev bootstrap) · provisions
read-only keys for every service sample plus overlay, and a
read-write key for platform. Runs as part of make seed.
Permission model
- Read-only on application repos and on the overlay repo. Agents read code for analysis; they do not write to your application code.
- Pull-request write only on platform and overlay repos. Agents
push feature branches and open PRs. They cannot push to
main. They cannot delete branches. They cannot force-push.
Git API token (Gitea or GitHub App)
For opening pull requests, the Runner needs an API credential. Two shapes, depending on provider.
Gitea (local-dev)
A Gitea API token stored as the K8s Secret gitea-api-token in
trustacks-system. Provisioned automatically by
scripts/setup_runner_access.py during make seed. The DevOps
Engineer agent uses it to open PRs against the platform repo.
GitHub App (production-shaped)
The Runner clones via HTTPS using a GitHub App installation token (per ADR-0018 in trustacks-mvp ). This requires a one-time operator step.
- Register the TruStacks GitHub App on github.com.
- Apply the
trustacks-github-appSecret intrustacks-systemwith three fields:app-id,private-key.pem,webhook-secret. - Set the CP environment variable
TRUSTACKS_CP_GITHUB_APP_SLUGto your App’s URL slug.
Until that operator setup completes, the Connect-a-repo wizard’s GitHub branch shows an amber “ask your admin” gate. That is the correct behavior.
After the App is registered, each customer installation auto-provisions its own installation ID through the wizard. The installation token is short-lived; the Runner re-mints it from the App private key on every clone.
Signed policy bundle
The constitution is shipped as a Cosign-signed OCI artifact. The Runner’s init container pulls the bundle and verifies the signature before extracting it; an unsigned or tampered bundle never reaches the OPA evaluator.
Where the bundle lives
- Local-dev · pushed by
make policy-bundleto your in-cluster Zot atzot.trustacks-system.svc.cluster.local:5000/trustacks/policy/constitution:dev. - Production · pulled from the TruStacks registry by the Runner on startup; the bundle version is pinned per Runner release (see the constitution reference).
How signing works
make policy-bundle runs three steps:
opa build -b policy/constitutionbuilds the bundle.oras pushuploads it to Zot.cosign signsigns it using the project-local keypair at.policy-keys/(auto-generated bymake policy-keyson first run).
The public key is applied as the policy-pub-key ConfigMap (via
make policy-pub-deploy). The Runner’s init container reads the
ConfigMap and runs cosign verify --key /policy-pub/cosign.pub against
each pulled bundle.
Fallback behavior
If the bundle pull fails (Zot unreachable, signature does not verify),
the Runner falls back to the image-baked copy under
/etc/trustacks/policy/. The cluster stays functional during a Zot
wipe, but the UI’s /rules page comes up empty until the bundle is
republished, since the rule inventory ships inside the bundle.
To run with an unsigned bundle (CI without keys, hotfix), set
policyBundle.verify=false in the chart values. Do not do this in
production.
Optional integrations
These are connected through the Environment Profile rather than as required Runner secrets. They are needed only if the corresponding agent wants them.
| Secret | Used by | How declared |
|---|---|---|
| Container registry pull credentials | Image scanning, build provenance | Environment Profile entry + K8s Secret |
| SAST or scanner API tokens (Snyk, SonarQube, etc.) | Baseline Security agent | Environment Profile entry + K8s Secret |
| Observability tokens (Datadog, New Relic, etc.) | SRE Specialist, Cluster Operator | Environment Profile entry + MCP server config |
| Ticketing tokens (Jira, ServiceNow, Linear) | Coordinator (change records, links) | Environment Profile entry + MCP server config |
See the Environment Profile section of architecture for the full category list.
What the Runner does not hold
Listed explicitly because it is the trust property the platform rests on.
- No cluster admin credentials. The Runner has a service account scoped to its own namespace. It cannot reach the API server outside that scope.
- No cloud provider keys. No AWS, no GCP, no Azure. Cloud-resource changes happen through the GitOps controller after a human merges the PR.
- No production database credentials. Secrets bound to running services are referenced by name in the manifests the agent emits; the agent never resolves them.
- No write access to your
mainbranch. Every change is a PR against a feature branch. Every PR requires a human approval to merge.
Where to go next
- Local development install · the make-up + make-seed flow that provisions every Secret on first boot
- GitOps integration · how ArgoCD picks up the PRs and what credentials it needs (separate from the Runner)
- Architecture · Data Plane · the trust boundary in context