An operating system does four things for the programs that run on it. It says who each one is. It decides what each one may touch. It keeps a record of what each one did. And when a program needs something only the person at the keyboard can give, it stops and asks.
AI agents need the same four things, and today most tools give them none of them. An agent connects with a key, and the key is a person: it can do everything that person can, for as long as the key lives, and what it did looks exactly like what the person did. That works while agents draft and suggest. It does not work once they do real work.
This post is about where CodeKasten is going: a workspace that does for agents what an operating system does for programs. It describes a design we are building in stages, not what you can use today. Some of it, like branches that agents work on and people merge, is already here.
NOTE
Everything below is a design we are building, written as if it were done so it is easier to read. The first stages are under way. We will write about each one as it ships.
What we learned by watching our own agents
CodeKasten is built with Claude Code, and its coding sessions are kept in CodeKasten. So before designing anything, we read eight days of our own sessions: 3,243 tool calls, 239 prompts and 54 pieces of work.
Three things stood out.
- Agents use CodeKasten for bookkeeping, and do the work somewhere else. Only 4% of the calls were CodeKasten's, and most of those were summaries, gaps, logs and notes. The rest was the shell: files, tests, git, deploys.
- People mostly said yes. Of the 175 times a person stepped in, more than half were approvals: "yes", "commit", "merged", "ship it". Nobody had to stop a runaway agent.
- A key was a person. Agents' changes could not be told from people's. Knowledge an agent wrote went straight into every later session. Our own injection test showed four ways a hostile record could get through.
So CodeKasten should not try to be everything an agent does. It should be the three things an agent cannot give itself: the authority that says what it may do, the ledger of what it did, and the place where people decide.
Agents you can name
Every agent gets its own identity in the workspace, tied to the person it works for. When it creates or changes a record, the record says so: not "changed by Linh", but "changed by Claude, for Linh". Workflows already work this way. A record a workflow writes names the workflow, and now a record an agent writes names the agent.
That one change answers questions nobody could answer before. What did this session change? Which commits on this branch were the agent's, and which were mine? When a review shows 214 changed records, you can see which ones the agent made.
A key for the bookkeeping, and a key for the work
When an agent works on a project, it does two kinds of things in CodeKasten. It keeps the books: what the session did, the facts it learned, the gaps it found, the features it worked on. And sometimes it does the work itself: it builds an app, writes a post, or changes a customer's records.
These want opposite permissions, so they get different keys.
| Bookkeeping key | Work key | |
|---|---|---|
| For | the session's own record-keeping | changing a product: an app, records, a blog |
| Writes | its notes, facts, gaps and features, nothing else | the record types the work needs |
| Reads | your past sessions and the project's knowledge | the work's records, never your transcripts |
| Named | once per machine and workspace | by the work it does: "the blog key" |
A session holds one bookkeeping key and as many work keys as its work needs. You attach a work key from the Inbox when the agent asks for it. The key itself never passes through the conversation.
Splitting keys this way does more than keep things tidy. An attack needs three things at once: private data, text an attacker wrote, and a way to send something out. The bookkeeping key holds the private data but can write nothing that leaves. The work key changes the product but cannot read your sessions. A hostile blog draft read with the blog key cannot reach your coding history.
Permissions that only get narrower
A key is the root of a grant: what an agent may do, written as conditions that must all hold. Which workspace and which branches. Which record types, for reading and for writing. Which actions: write, commit, ask, run a workflow. Who it may message. When it expires. How much it may spend.
Anyone holding a grant can make a narrower one: a coding session hands a subagent a grant that can only read, or only touch one type. A condition can be added, never removed, so a narrower grant can never be widened again. Revoke a grant and every grant made from it stops too. You see your agents' grants on one page and revoke them there.
An agent's authority is always the overlap of two things: what its person may do, and what its grant allows. It never outranks its person. That is also why finer permissions can come later without changing any of this. If CodeKasten one day lets you limit people by record type, by field or by row, an agent's authority simply shrinks with its person's.
A small set of calls
Today an agent gets more than sixty tools, one per kind of thing and kind of change. Each one checks permissions in its own way, and that is how mistakes creep in.
The core becomes a small, versioned set of calls: who am I, what types are here, find, read, write (a batch of changes in one transaction), define a type, branch, ask, run, upload, watch for changes, take a lease, and narrow a grant. Every call declares once what it does: nothing, a change, something destructive, or something outside CodeKasten. What your client shows you, and whether it asks before running a call, comes from that declaration. The friendly tools agents know by name, like "open a gap" or "save a fact", stay, but they are built on the core and check nothing of their own.
Knowledge an agent proposes and a person accepts
Every session starts with a briefing: the project's profile, its open gaps, the facts the team has settled, and what you told the agent about how you work. Agents trust the briefing, because it arrives as the session's own context. That makes it the most valuable thing an attacker could write to.
So a fact or gap an agent writes starts as proposed. If an agent wants to change or remove a fact the team already accepted, that becomes a proposed change too, and the fact stays as it was until someone accepts. At the end of a session, you get one Inbox item with everything it proposed. Accept it all, accept some, or drop it. Only accepted knowledge reaches the briefing, so a hostile record can no longer plant a "fact" in every teammate's next session.
Knowledge is not merged like records. It is shared across branches the moment it is accepted, because a fact about the project is true on every branch.
Private branches, until you ask
Agents still never write to a protected branch. A session's first change opens a branch of its own, it commits there, and a person merges it. Two things change.
The branch is private until a merge is asked. Nobody else lists it, reads it or writes to it, not even admins. When the agent asks for a merge, the people it asks can read it, so they see exactly what they are reviewing, and nobody can slip a change in before they do.
A session also opens a branch only when it changes a versioned record. Bookkeeping needs none, so most sessions open no branch at all. A merged branch is closed, and one nobody merged is closed after two weeks, after asking its person.
Asking a person, as a call
More than half of what people did in our sessions was approve something. Most of those approvals happened in a chat window and were gone when the window closed.
Ask makes the Inbox a call an agent can make and wait on. The agent asks a question with its choices ("merge this branch?", "attach the blog key?", "ship this release?"), and the people allowed to answer get it in their Inbox. If an answer comes within a few seconds, the agent carries on. If not, it gets a ticket and checks back later. A secret is never asked for in the conversation: the agent sends you to a page in the app instead. Every approval that unlocks something is kept as a decision, with who made it and when.
Effects held until the merge
Some steps reach outside CodeKasten: a connector sends an email, posts to another service, calls a webhook. When an agent runs a workflow on its branch, the steps that only change data run there as usual. The steps that reach outside are held. They run once, when a person merges the branch, or when someone releases them by hand.
Merging a branch then means one clear thing: accept this agent's work. Its records, and the effects it wants to have on the world, together.
Leases and budgets, later
Two agents changing the same record at the same time will need a way not to collide. We designed leases, with an epoch that makes a stale change fail at merge instead of silently overwriting. We checked the design with a model checker across eleven million states before writing any code. Budgets, pause and kill come with them.
They come last on purpose. In our own sessions no two agents worked on one workspace at once, and none ran away. Approvals and injection paths were the real problems, so those are fixed first.
What CodeKasten will not be
Being clear about the limits matters as much as the features.
- Not a shell. Agents run commands in their own environment, and their client decides what is allowed there. CodeKasten records what was done and decides what is asked of it.
- Not a model runtime. CodeKasten does not host agents or pick models. Agents are clients, and workflows call models as steps.
- Not a guard that guesses. There is no classifier trying to spot prompt injection. Catching 95% of attacks is a failing grade. The design removes what an attack needs instead.
- Never above its person. There are no service accounts with more power than a member, and no agent ever writes to a protected branch.
How we will know it works
Every stage has to pass three checks before it ships. Our injection test plays an agent that obeys a hostile record word for word and checks that CodeKasten refuses. Today it marks the four holes it still finds, each with the stage that closes it. We will replay real past work, with hidden tests, to make sure agents get no worse. And the model checker has to keep finding nothing wrong.
The kernel this describes is smaller than "everything an agent does". It is who an agent is, what it may do, what it did, and the moment a person says yes. Those are the parts an agent cannot do for itself, and the parts that let you hand it real work.
