“Whose files are these?”
Every agent run shares one box. One run's half-finished branch and stray files leak into the next.
On Kubernetes, every isolated ConvOps run is its own Job: a fresh pod with its own volume and secret, locked down, time limited, and gone when the work is done.
one job per run · per-run secret · no service-account token · pod security restricted
One Kubernetes Job per run: a fresh pod with its own volume and Secret, no service-account token, Pod Security restricted, isolated by NetworkPolicy and deleted when the run ends.
ConvOps is the operations layer for AI agents: an MCP server that holds your team's process as workflows, with approval gates, a shared memory and an audit trail. It runs no AI models.
updated
Agents on a shared server or a laptop share its disk, its keys and its fate. Nobody owns the blast radius.
“Whose files are these?”
Every agent run shares one box. One run's half-finished branch and stray files leak into the next.
“Who has the keys?”
Tokens live on laptops and shared servers, readable by every job that runs there.
“Can anyone stop it?”
A runaway agent keeps going until someone notices, finds the box and kills the process.
“Where did the work go?”
The box restarts and two hours of unpushed work vanish with it.
An example task, Fix refund rounding, routed to an isolated executor. Follow the pod from creation to deletion.
A step is routed to an isolated executor. ConvOps creates a Kubernetes Job for this run, mounts the task volume and a secret made for this run only.
The MCP servers the environment names start as sidecars inside the pod. Once they answer, the agent starts in the main container.
No service-account token. Non-root, every capability dropped, Pod Security restricted. No ingress, limited egress. Then the agent gets to work.
A clean fast-forward to the branch you named, or a task branch for review. Commits carry the identity you set on the environment.
If the run fails, nothing is thrown away. The commits go to a rescue branch, and the task volume is still there to resume.
The pod is deleted. What remains is the run record: tokens, model split, cost, turns, duration, and the error class if it failed.
One pod per run. Each part is there for a reason, and each one is gone when the run ends, except the work.
run pod · example
The working copy. Kept across the task's steps, so the next step and any resume pick up where this one stopped.
A Kubernetes Secret made for this run. Values are masked in every log and transcript.
MCP servers start as native sidecars before the agent, with per-tool allow and deny.
No service-account token is mounted. The agent cannot talk to the Kubernetes API.
Non-root, every capability dropped, under the Pod Security restricted profile.
No ingress at all. Egress limited to what the run needs.
Set per executor, 4 hours by default. The run cannot outlive it.
A clean fast-forward when history allows. A task branch when it does not. A rescue branch when the run fails.
Every executor has a time limit, 4 hours by default. Stop works on any run. A run that hits its limit resumes where it was.
You saw it heading the wrong way and pressed Stop. The pod ends and the run is recorded as stopped.
The run hit its time limit. It resumed on the same task volume and finished without starting over.
Enterprise runs the whole system on its own Kubernetes. Runs, environments, secrets and records stay inside your cluster.
ConvOps cloud We run everything. Your AI clients and models stay yours and connect over MCP.
ConvOps runs in your Kubernetes from one Helm chart. Runs are Jobs in a namespace you own.
Bring your own model provider and keys. Git access, secrets and MCP servers stay yours.
Code, secrets, transcripts and run records stay inside your cluster.
One Job per run. A fresh pod with no cluster credentials, no ingress and nothing from the run before.
one run, one pod
The working copy lives on a per-task volume. It outlasts the pod, so steps continue and runs resume.
kept across steps
MCP servers run beside the agent in the same pod, started first, with per-tool allow and deny.
native sidecars
A failed run never loses its work. The commits are pushed to a rescue branch you can pick up.
failure keeps the work
One Job per run. No service-account token, non-root, capabilities dropped, a deadline from the executor time limit, MCP servers as native sidecars (init containers with restartPolicy Always).
# run pod spec (example, trimmed)
apiVersion: batch/v1
kind: Job
metadata:
name: run-7f3a
namespace: convops-runs
spec:
backoffLimit: 0
activeDeadlineSeconds: 14400 # executor time limit
template:
spec:
automountServiceAccountToken: false
restartPolicy: Never
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
initContainers:
- name: github-mcp # native sidecar
image: example/github-mcp
restartPolicy: Always
securityContext:
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
containers:
- name: agent
image: example/agent-claude-code
securityContext:
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
envFrom:
- secretRef: { name: run-7f3a }
volumeMounts:
- { name: task, mountPath: /work }
volumes:
- name: task
persistentVolumeClaim:
claimName: task-8c1eSelf-hosted installs set the run namespace, posture, network policy, time limit and model credentials in one Helm values file.
# values.yaml (example)
runs:
namespace: convops-runs
podSecurity: restricted
networkPolicy:
ingress: none
egress: limited
timeLimit: 4h
taskVolume:
size: 10Gi
models:
existingSecret: model-credentialsEvery pod in the run namespace is selected by a policy that admits nothing in and lets only the egress the run needs out.
# run namespace network policy (example)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: runs-isolation
namespace: convops-runs
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress: [] # no ingress at all
egress:
- ports:
- { port: 443, protocol: TCP }When the pod is gone, the run record remains: tokens, per-model split, cost, turns, duration and the error class.
{
"task": "Fix refund rounding",
"executor": "isolated-sonnet",
"status": "succeeded",
"pushed": "convops/task/8c1e",
"tokens": { "input": 151200, "output": 30800 },
"models": { "sonnet": 0.91, "haiku": 0.09 },
"cost_usd": 1.94,
"turns": 34,
"duration_s": 700,
"outcome": "succeeded"
}No. Run pods are created without a service-account token, so the agent has no credentials for the cluster API. The pod runs as non-root, with every Linux capability dropped, under the Pod Security restricted profile.
A NetworkPolicy isolates every run pod: no ingress at all, and egress limited to what the run needs. MCP servers it uses can run as sidecars inside the same pod, so those calls never leave it.
Secrets are stored once through a one-time link, never pasted in chat. Each run gets its own Kubernetes Secret, mounted for that run only, and the values are masked in every log and transcript.
Every executor has a time limit (4 hours by default) and Stop works on any run. A run that hits its limit can resume on the same volume. A failed run keeps its work on a rescue branch.
The pod is deleted when the run ends. The task volume stays so the next step continues from the same working copy. The run record stays too: tokens, model split, cost, turns, duration and any error class.
Yes. Enterprise customers self-host ConvOps on their own Kubernetes with one Helm chart. Runs, environments and secrets stay in your cluster, with your own models and credentials. Nothing has to leave.
Claude Code and OpenCode today. The design is agent-neutral, so Codex, Kimi and others plug in the same way: an engine in the main container, your environment around it.