Kibotos
Scenarios

Coding Agent with a persistent project

A coding Agent needs its project to survive when a session ends. This page keeps the project on a Volume of its own and gives each session a new Workspace bound to that Volume. The complete program is examples/go/coding-agent.

Prerequisites

Steps

1. Create the project's Volume

The Volume belongs to the project. A label lets your platform find it again after a restart:

created, err := client.Volumes().CreateVolume(ctx, connect.NewRequest(&ctrlv1.CreateVolumeRequest{
	Metadata: &ctrlv1.CommandMetadata{CommandId: kibotos.NewCommandID("coding-agent")},
	Spec:     &ctrlv1.VolumeSpec{CapacityBytes: 1 << 30},
	Labels:   map[string]string{"example.com/project": "demo"},
}))

2. Bind it to a session's Workspace

Each session creates a Workspace bound to the Volume. The expiration time keeps a crashed platform from leaving the Workspace running:

request := &ctrlv1.CreateWorkspaceRequest{
	Metadata: &ctrlv1.CommandMetadata{CommandId: commandID},
	Spec: &ctrlv1.WorkspaceCreateSpec{
		Template:  &ctrlv1.TemplateSelector{TemplateId: templateID},
		Resources: &ctrlv1.ResourceRequest{CpuMillis: 1000, MemoryBytes: 512 << 20},
		Volume: &ctrlv1.WorkspaceVolumeBinding{
			Mode:      ctrlv1.WorkspaceVolumeMode_WORKSPACE_VOLUME_MODE_BOUND,
			VolumeId:  volumeID,
			MountPath: "/workspace",
		},
	},
	Expiration: &ctrlv1.WorkspaceExpirationPolicy{
		Timeout: durationpb.New(time.Hour),
		Action:  ctrlv1.WorkspaceExpirationAction_WORKSPACE_EXPIRATION_ACTION_DELETE,
	},
}

3. Work in the project directory

The session reads what earlier sessions left and runs its tools in the project directory. Set WorkingDirectory on each process:

stream, err := session.StartProcess(ctx, &ctrlv1.StartRequest{
	Argv:             argv,
	WorkingDirectory: "/workspace",
	Mode:             &ctrlv1.StartRequest_Pipe{Pipe: &ctrlv1.ProcessPipeMode{}},
})

4. End the session

Delete the Workspace and wait for the delete command to settle. deleteWorkspace in the example ends with Client.WaitWorkspaceDeleted. The Volume stays, because a binding to an existing Volume is not deleted with the Workspace. The next session binds it again:

if err := deleteWorkspace(ctx, client, workspaceID); err != nil {
	return err
}

5. Run the program

The program runs two sessions in a row and deletes the Volume at the end. Your platform keeps the Volume for as long as the project exists:

go run ./examples/go/coding-agent -endpoint https://ctrl.example.com:18092 \
  -identity "$HOME/.local/state/kibotos-coding-agent" -enrollment-key-file coding-agent.key

The second session finds what the first one wrote:

created Volume vol-06gvz1fz8nr2k87mzsg8741qwg for the project
first session runs in Workspace ws-06gvz1fzpxy0d6qx59degv74q8
the project is empty
second session runs in Workspace ws-06gvz1gdyxrk76207y0ajds63w
previous sessions left:
first session worked on ws-06gvz1fzpxy0d6qx59degv74q8

With the E2B SDK

A Volume created by name works the same way. Mount it with volumeMounts, and deleting the sandbox keeps it:

import { Sandbox, Volume } from 'e2b';

const volume = await Volume.create('project-demo');

const first = await Sandbox.create('kibotos-minimal', {
  volumeMounts: { '/workspace': volume },
});
await first.files.write('/workspace/progress.log', 'first session\n');
await first.kill();

const second = await Sandbox.create('kibotos-minimal', {
  volumeMounts: { '/workspace': volume },
});
console.log(await second.files.read('/workspace/progress.log'));
await second.kill();

Repeating Volume.create with the same name returns the same Volume, so you can call it at the start of each session. See Volumes.

Notes

  • Only data on the Volume is guaranteed to survive. Packages installed elsewhere on the root file system are lost with the Workspace, so install them in a Template or on the Volume.
  • The storage pool of a Volume decides which nodes can run a Workspace bound to it. See Volumes.
  • A Workspace bound this way has no network access until you allow it. See Network policy.

Next steps

On this page