Like OpenClaw, but for teams
A hands-on introduction to Kodama, a free and open-source software factory for teams. Ten minutes on one Linux machine, then the path to your company's Kubernetes.
A coding agent in a terminal is a single-player tool. It has one developer, one laptop, one set of credentials, and one conversation that is gone when the window closes.
A company is not shaped like that. It has hundreds of repositories, some of which nobody wants to touch. It has people who may start work, others who must review it, and others who are called when it fails. It has teams, or customers, who must never see each other's code. And it has a Kubernetes cluster that already runs everything else.
Kodama is a software factory built for that shape. It is multi-tenant, role-based and API-first. It runs on the cluster you already operate. Its pipelines take a task to a tested, merged change, with a human at the points you choose, or at none. It is written in Go and licensed Apache-2.0, and this post is the first time we are introducing it.
The usual word for all this is enterprise-grade, and it is usually an adjective with nothing behind it. So this post runs the thing first, then lists what is behind the word, and ends with what does not work yet.
The whole model in one sentence
A pipeline is an ordered list of actions; an action runs a container or requests an answer; an execution runs a pipeline once; a task is a conversation that starts executions.
If you have used Drone, or any container CI, you know the first half already: containers run in order, share a workspace, and exit zero or do not. The second half is the new part. An action can run no container at all and ask a role for a decision instead. An agent is one more container.
That is the factory. Engineers build the line (pipelines, credentials, rules). Anyone submits tasks: a person, a script, another system. Containers and agents do the work. Reviewers and operators each have an inbox.
1. Start a local Kodama
You need Linux with rootless Podman 5 or newer and its user socket (systemctl --user enable --now podman.socket), curl, tar, and port 8443 free.
curl -fsSL https://raw.githubusercontent.com/optimiware/kodama/master/install.sh | sh kodama up source ~/.kodama/instances/default/env.sh kodactl whoami If piping curl into a shell bothers you, read install.sh first. It is about a hundred lines, checks both downloads against the release's SHA256SUMS, installs into ~/.local/bin, and never asks for root.
kodama up starts PostgreSQL, NATS, the Kodama server and a Git service as containers. It creates a local CA, one tenant, one project and four people: an admin, an engineer, a reviewer and an operator. whoami should end with:
roles admin,engineer,reviewer,operator 2. Run a container
podman pull docker.io/library/alpine:3.24@sha256:0f49f331d9212aff11b932863be4eb01cdf962969aa86dab7d1ee21ac6f5f52b cat > hello.yaml <<'EOF' actions: - name: hello image: docker.io/library/alpine@sha256:0f49f331d9212aff11b932863be4eb01cdf962969aa86dab7d1ee21ac6f5f52b timeout: 2m commands: - echo "hello from a Kodama workload" EOF kodactl run hello.yaml execution execution-… started hello from a Kodama workload execution execution-… succeeded The image is pinned by digest and you pull it yourself, because the local runtime never pulls on your behalf. No runner binary is injected into the container: Kodama replaces the entrypoint with a fail-fast script that the image's own shell runs.
3. Work as two people
So far you were the admin, who holds every role. Become the engineer:
unset KODAMA_CREDENTIAL_FILE kodactl login # choose Engineer; add --device on a machine without a browser kodactl whoami roles engineer This pipeline drafts a change, asks a reviewer, and ships only after a yes:
cat > ship.yaml <<'EOF' inputs: [change] actions: - name: draft image: docker.io/library/alpine@sha256:0f49f331d9212aff11b932863be4eb01cdf962969aa86dab7d1ee21ac6f5f52b timeout: 2m commands: - cp /kodama/inputs/change /workspace/change.txt - echo "drafted $(wc -c < /workspace/change.txt) bytes in /workspace" - name: review request: role/reviewer timeout: 10m subject: operation: demo.ship resource: local-demo change: {from: inputs.change} - name: ship image: docker.io/library/alpine@sha256:0f49f331d9212aff11b932863be4eb01cdf962969aa86dab7d1ee21ac6f5f52b timeout: 2m commands: - echo "shipping -> $(cat /workspace/change.txt)" EOF kodactl run ship.yaml --input change="handle empty carts" execution execution-… started execution execution-… is waiting for role/reviewer to answer request "review"; see 'kodactl inbox' and 'kodactl approve execution-…' You started the work. Try to sign it off:
kodactl approve <execution-id> error: forbidden: current authority check "membership" did not hold The request went to a role, and you do not hold it. It now sits in the inbox of everyone who does. No container runs while it waits, which can be up to thirty days: podman ps shows the four containers of the stack and no workload. The workspace is kept, and the execution's credential is revoked until someone answers.
Now be the second person. The local admin holds the Reviewer role:
source ~/.kodama/instances/default/env.sh kodactl inbox kodactl approve <execution-id> kodactl watch <execution-id> kodactl logs <execution-id> --action ship KIND TASK EXECUTION ACTION PROMPT/SUMMARY request - execution-… act-… [role/reviewer] change=handle empty carts operation=demo.ship resource=local-demo shipping -> handle empty carts ship ran in a new container and read the file draft wrote before the wait. kodactl inspect <execution-id> shows the decision, who made it and when. A rejection (kodactl reject <execution-id> --reason "…") fails the execution and ship never starts. A request that times out fails too. Nothing in Kodama reads silence as approval.
4. Give the factory a task
run stores nothing. A team stores its pipelines and starts them from tasks:
kodactl pipeline apply hello.yaml # prints the pipeline id kodactl task run --title "First task" --pipeline <pipeline-id> kodactl task list A task is the unit a team shares. It has a title, a description, a conversation and metadata of your own, and it keeps the history of every execution it started. Tasks arrive through one API with two edges, REST and MCP, so a person, a script, an email adapter or the agent in your editor (kodactl mcp install claude --print) files work the same way and under the same authorization.
Two things make a task more than a ticket:
- An agent can ask. A workload that gets stuck puts a question on its task, addressed to a role or to a service. Kodama cancels the execution, the question lands in that inbox, and the reply starts a fresh execution with the whole conversation in
/kodama/task.json. Nobody keeps a container warm for three days. - Failures have an owner. A task whose execution failed appears in the operators' inbox, where someone starts a fresh attempt or cancels it.
5. A real line
An agent is a container command. Claude Code, Codex, OpenCode, Pi or a shell script all run the same way. A pipeline that delivers a change looks like this; the images and commands are placeholders for your own:
inputs: [repository, target_ref, expected_base] actions: - name: implement image: registry.example.com/coding-agent@sha256:… outputs: [candidate] commands: - implement-and-push - name: test image: registry.example.com/test-tools@sha256:… commands: - test-candidate "$(cat /kodama/outputs/implement/candidate)" - name: review request: role/reviewer timeout: 72h subject: operation: git.ref.update resource: {from: inputs.repository} ref: {from: inputs.target_ref} candidate: {from: implement.outputs.candidate} expected_base: {from: inputs.expected_base} - name: integrate image: registry.example.com/git-tools@sha256:… commands: - integrate-candidate How much of this is automatic is your decision, and you make it per repository and per branch:
- A person decides. Keep the
reviewaction. The Git service refuses the push tomainunless this execution holds a passed review of that exact commit, on that ref, from that base. - A program decides. Assign the request to
service/<id>and a validator, a merge queue or a policy engine answers it through the API. - Nobody decides. Remove the request and the approval rule, and the pipeline runs from task to merge unattended.
The rules that say which of these applies live in a Git repository, in the spirit of gitolite, so changing one is a commit with a name on it:
repo shop/web R = @all RW+ refs/heads/work/ = @engineer @execution RW refs/heads/main = @execution option approve.refs/heads/main = reviewer The agent never sees your model provider's API key. It points ANTHROPIC_BASE_URL or OPENAI_BASE_URL at Kodama's model proxy and authenticates with its execution token. The key stays in the control plane, and the token dies with the execution. The recipe is in external-harness.yaml.
6. What is behind "enterprise-grade"
Kodama is multi-tenant by design. A tenant is an isolated organization: a business unit, a subsidiary, a customer. Projects live inside it. Isolation is the same boundary drawn again at every layer:
- Data. Every record is owned by a tenant and a project. PostgreSQL enforces it with forced row-level security, under a runtime role that does not own the tables.
- Execution. On Kubernetes each tenant has its own namespace, a default-deny NetworkPolicy and a LimitRange. A workload of one tenant cannot reach a workload of another, or the Kubernetes API.
- Credentials. Each tenant has its own derivation key. An execution's token is derived from it, is valid for at most eight hours at a time, and is revoked while the execution waits and when it ends. Rotating one tenant's key invalidates that tenant's tokens and nobody else's.
- Secrets. Values are envelope-encrypted under a key in your cloud KMS. The management API returns metadata, never the value. A container gets a secret as a file, and only if its action declares it.
- Git. Each tenant has its own Git workers and volumes. Workloads never mount them.
- Logs and events. Both are scoped by the same ownership, in the API and on the broker.
We check this on a deployed cluster, not only in unit tests. The qualification probes run a workload in one tenant that tries to reach a listener in another, and use one tenant's credentials against the other's pipelines, tasks, secret metadata and logs.
The rest of what a company asks for is in the same place:
- Identity. People sign in through your OIDC provider. Five roles (Admin, Engineer, Reviewer, Operator, Viewer) are granted per tenant and per project. A role change applies on the next call. The Admin role grants no review authority.
- Audit. One function authorizes every operation. Decisions keep their actor and time. An execution keeps the exact pipeline bytes it was admitted with, their SHA-256 and the image digests, so you can say what ran.
- Recovery. PostgreSQL records facts and NATS only distributes them, so a broker outage delays integrations and does not stop executions. After a crash the controller observes what it had started. It never reruns a command it cannot prove did not run.
Honest boundary: this is isolation around ordinary containers, for teams and business units that trust their own engineers. It is not a sandbox for running strangers' code.
7. On your Kubernetes
The laptop runs workloads on Podman. Production runs them on Kubernetes, with the primitives you already operate:
- Each container action is a plain Job. Kodama adds no CRDs and no operator.
- The workspace is a volume claim, kept across the Jobs of one execution and across its approval waits.
- The control plane is one Deployment, installed with Helm. A second chart adds NATS JetStream.
- OpenTofu roots for Scaleway, AWS and Google Cloud create the cluster, managed PostgreSQL and the KMS key.
Your code, your pipelines and your decisions stay in your cluster and your database. What leaves is what your agents send to the model provider you configure. The pipeline YAML is the same on the laptop and in production.
This is also where the scale comes from. An execution is a row in PostgreSQL and its work is Jobs, so a thousand tasks are a thousand rows and whatever your scheduler and autoscaler make of the Jobs. Work that waits for a person holds a volume and no CPU. The production deployment guide has the procedure.
8. The job we would give it first: legacy modernization
Modernizing an old estate is the same change made four hundred times, each needing a build, a test run and a signature. It is too large for a team and too repetitive for anyone to want. It is shaped like a factory order:
- Write what the agents must know (architecture, domain language, conventions) as Markdown in a context repository. The Git service serves it under the same access rules as the code.
- Write one pipeline: implement, build, test, review, integrate.
- Create one task per module from a script, through the API.
- Reviewers work an inbox instead of a backlog. Operators see what failed. Every merged commit leads back to a task, an execution and an approver.
When the reviewers trust a class of change, move it to the unattended line.
What we left out
The spec spends a lot of its length on what Kodama refuses to do:
- No
when, no expressions, no parallel actions, no DAG, no loops. A pipeline is a list. A condition belongs in a shell script, whereifalready works and can be tested. - No resume. Each action settles once. Rework is a new execution from the first action, and the old one stays in the history as it was.
- No pipelines discovered in Git. You post them through the API (
kodactl pipeline apply .kodama/). A workload that can push code cannot change the pipeline that governs it.
We expect arguments about the first one.
The name
A kodama (木霊) is a tree spirit of Japanese folklore. It lives in a tree and is seldom seen.
Unseen, the wood thinks
branches commit while you sleep
dawn finds the leaves changed
Try it, and tell us where it breaks
- The repository: github.com/optimiware/kodama
- The design, in one document with 20 numbered acceptance criteria: KODAMA.md
- The rest of the tour (sign-in, Git, MCP): quickstart
When you are done, kodama down --purge removes the containers, the volumes and the state.
We would most like to hear from people who run platform teams: what your company would need before it let this near a real repository, and what broke when you ran the steps above. Open an issue.
