Credentials
What is held where, what reaches a workspace, and the trade-off settled least comfortably.
| What | Where it lives | Reaches a workspace as |
|---|---|---|
| Proxmox API token | .env | never leaves the controller |
| GitHub App key | file on disk, 0600 | a repo-scoped token, renewed hourly |
| Claude subscription token | .env | ~/.config/agent-env, sourced by the runner |
Both workspace credentials arrive on stdin, never as arguments, so neither appears in the process list. Git reads its credential from a store rather than a URL, because git repeats the remote it was using in its errors and those reach the timeline the UI renders.
ssh does not preserve argument boundaries — it joins the command and the remote shell re-splits
it — so everything sent is quoted before it leaves.
Proxmox
Use a dedicated Proxmox user and privilege-separated API token. The controller holds it and nothing else does: it never reaches a workspace, and no agent can ask for it.
VM.Audit
VM.Clone
VM.Allocate
VM.PowerMgmt
VM.Config.Options
VM.Config.CPU
VM.Config.Memory
VM.Config.Network
Datastore.AllocateSpace
SDN.UseAdd VM.Config.Disk only if the controller changes disk configuration.
Scope permissions to the golden template (audit and clone), the pre-created
disposable-workspaces pool, the clone storage, and the specific bridge or VNet. Do not grant
broad administrator, console, permission-management, or host-modification privileges.
The template
Do not bake secrets into the LXC template.
| Safe | Unsafe |
|---|---|
| Controller SSH public key | Proxmox API tokens |
| SSH user CA public key | GitHub access tokens or private deploy keys |
| SSH host CA public key | Model-provider API keys |
| Public TLS trust roots | Claude, Codex, or GitHub login state |
| Non-secret bootstrap configuration |
LXC cloning does not provide QEMU-style cloud-init, and Proxmox does not expose a general REST
pct exec or pct push. The initial bootstrap channel must therefore be prepared in the template
or supplied through a separately controlled Proxmox-host mechanism.
Bake a dedicated controller public key for the non-root agent account. Keep its private key only
on the hosted controller.
Git
GitHub App installation tokens, minted per repository and lasting an hour.
- Mint a token scoped to that repository alone.
- Store it with git's
credential.helper store, written from stdin so it is never an argument and never appears inpson the workspace. - Clone over HTTPS from a credential-free URL and let git read the stored value itself.
- Replace the stored credential before the hour is up, for as long as the workspace lives.
The token is stored rather than used once and deleted, which is a deliberate departure from the obvious design. The agent pushes later under its own steam, and a workspace outlives an installation token several times over. A refresh pass replaces it on a living workspace without touching its repository.
Never put the token in the URL. git repeats the remote it was using in its error text, and that text reaches the workspace timeline the UI renders. Everything git prints is scrubbed of the token at the boundary too, because one of those paths will be missed eventually.
Avoid forwarding the user's general SSH agent into an autonomous workspace. It grants broader signing and repository access than the task usually needs.
Model provider
Provider authentication is the largest unavoidable trade-off here, and the one that was settled least comfortably.
What is used: a Claude subscription OAuth token, held in .env on the controller and written
into each workspace as ~/.config/agent-env, which the runner sources explicitly. It arrives over
stdin, never as an argument. An API key was available and was deliberately not chosen.
It is the fourth option on the list below, and it is chosen knowingly: this is a single-operator deployment, the token is reusable rather than task-scoped, and any workspace that gets it can spend against the subscription. The container being disposable is what bounds that, not the credential.
Better, in order, if this ever stops being a single-operator tool:
- Short-lived, task-scoped provider credential.
- Controller-side credential broker that exchanges a workspace identity for limited access.
- Narrowly scoped API key injected as a mode
0600file. - Reusable login state, which is what is in use.
Track which credential classes were injected, never their values. Remove credential files during destruction even though deleting the LXC also removes its filesystem.
Last updated on