Kibotos

Commands and terminals

Run a single command in a Workspace and get its output back, or open an interactive terminal in the guest. Both resume by themselves when the connection to the node drops. The examples use the Go SDK, the E2B SDK, and kibo. Programs that call ProcessService directly are covered in With the API.

Running commands

Run a command in the guest, receive its standard output and standard error, and wait for it to finish. This example lists the contents of the Volume. The Go example uses the client from Connect to ctrl:

session, err := dataplane.Open(client, workspaceID, dataplane.WithScopes(
	ctrlv1.DataPlaneScope_DATA_PLANE_SCOPE_PROCESS,
))
if err != nil {
	log.Fatal(err)
}
defer session.Close()

stream, err := session.StartProcess(ctx, &ctrlv1.StartRequest{
	Argv:             []string{"sh", "-c", "ls -la"},
	WorkingDirectory: "/data",
	Mode:             &ctrlv1.StartRequest_Pipe{Pipe: &ctrlv1.ProcessPipeMode{}},
})
if err != nil {
	log.Fatal(err)
}
defer stream.Close()
for stream.Receive() {
	event := stream.Event()
	switch {
	case event.Output != nil:
		os.Stdout.Write(event.Output.GetData())
	case event.Exited != nil:
		fmt.Println("exit code", event.Exited.GetExitCode())
	}
}
if err := stream.Err(); err != nil {
	log.Fatal(err)
}

With the Go SDK and kibo, the command is not interpreted by a shell. When you need shell syntax, such as pipes or wildcards, write sh -c '...'. The E2B SDK takes one command string and runs it in a shell.

The E2B SDK throws CommandExitError when the command exits with a non-zero code. It carries exitCode.

The options of kibo workspace exec:

  • -w (--working-directory) and --user choose where and as whom the command runs. See User and working directory.
  • --exec-timeout kills the command after the given duration. There is no time limit by default.
  • For every flag, see kibo workspace exec.

-i forwards your local standard input to the remote process and closes the remote standard input when it reaches the end. This example copies a local directory into the Volume:

tar czf - src | kibo workspace exec -i "$workspace_id" -- tar xzf - -C /data

Exit codes

kibo workspace exec exits with the exit code of the remote process, as ssh does. A process killed by a signal gives 128 plus the signal number, and one killed by --exec-timeout gives 124.

When kibo itself fails, before or while following the command, it exits with 255. The error output names the cause. See kibo exit codes.

Opening a terminal

The kibo workspace terminal command opens an interactive terminal in the guest:

kibo workspace terminal "$workspace_id"

To run a specific program in the terminal, give it after --:

kibo workspace terminal "$workspace_id" -- /bin/bash -l
  • Both your local standard input and standard output must be terminals.
  • The initial size comes from your local terminal. To set it yourself, pass --rows and --columns together.
  • --working-directory and --user follow the same rules as for exec. See User and working directory.
  • Exit codes are the same as for exec.

Reconnection

When the connection to the node drops, exec and terminal resume automatically. They do not run the command again.

  • Output produced while disconnected is filled in.
  • Input that was not delivered is resent by position, with no duplicates.
  • Retries last at most 30 seconds. If the session cannot be resumed, the command exits with 255.

Lost output

The guest keeps recent output so that a resumed command can fill in what it missed:

  • Each output channel of each process keeps its most recent 1 MiB.
  • All processes in the guest together keep at most 16 MiB or 1/16 of guest memory, whichever is smaller, and never less than 1 MiB.
  • When the total is exceeded, the guest releases the buffers of other processes as a whole: exited processes first, then running processes whose latest output is oldest.

Output that is no longer kept when the command resumes cannot be recovered:

CommandWhat happens
execWrites to standard error which channel lost how many bytes, keeps receiving until the remote process ends, and then exits with 255. The error message includes the remote exit status. In --json output, the loss is a "type":"gap" record
terminalShows a notice in the terminal and continues. The exit code is still the remote one

User and working directory

Every request can choose which user it runs as in the guest, and process requests can also choose a working directory. File reads and writes choose their user by the same rules.

Entry pointUserWorking directory
kibo workspace exec--user-w, --working-directory
kibo workspace terminal--user--working-directory
kibo workspace file subcommands--userNot applicable
ProcessService/Startuserworking_directory
FilesystemService requestsuserNot applicable
/v1/filesQuery parameter userNot applicable

Choosing the user

  • Without a user, the request runs as the user the Template declares, or as root if the Template declares none.
  • root explicitly selects root.
  • The node checks any other user name against the Template's declaration.
  • If the user the Template declares is unavailable in the guest, see Default user unavailable.

Working directory and relative paths

  • Without a working directory, the process starts in the home directory of its user, such as /home/user or /root. Setting HOME as an environment variable, with -e or in the environment of ProcessService/Start, does not change this directory.
  • If the working directory does not exist, the request returns not_found.
  • Working directories and file paths can be relative. They resolve against the home directory of the user: for the user user, notes/a.txt means /home/user/notes/a.txt.
  • Paths in responses are always the resolved absolute paths.
  • Relative paths need a guest of v0.3.0 or later. On an older guest, file requests accept only absolute paths, and relative working directories are not resolved against home. Restart the Workspace to enable relative paths.

Important

The home directory is not a path whose data is guaranteed to be kept. To run in the Volume, pass -w /data. See Guarantees and limits.

With the API

Programs run processes through ProcessService on the data plane. They need a data-plane credential with the PROCESS scope; see Requesting a session credential. kibo requests the credentials it needs by itself. For every method and message, see ProcessService.

Starting a process

ProcessService/Start starts a process and returns its output stream. The process does not end with the Start or Attach stream. After the stream drops, the process keeps running, and the caller subscribes again with Attach.

After the process exits, SendInput, CloseStdin, SendSignal, and Resize on it return failed_precondition with:

  • ErrorDetail.reason set to CONFLICT
  • stage set to USING_PROCESS
  • no input_offset

The result of the process still arrives through exited on the output stream. Rely on that, and do not treat this error as a session failure.

Resuming process streams

Calling Attach again fills in the output missed during the disconnect, as far as the guest still keeps it, and input can be resent safely. For how much output is kept, see Lost output. kibo and the E2B gateway do this automatically; see Reconnection and E2B compatibility.

Resuming needs a guest of v0.2.0 or later. On an older guest, Attach ignores resume_from and the subscription starts at the current position, with start equal to offset, and SendInput with input_offset returns unimplemented (UNSUPPORTED). Restart the Workspace to enable resuming.

To resume a stream:

  1. Track the position each channel has received: the offset of the ProcessOutputEvent plus the length of its data.
  2. When the stream ends with unavailable or a dropped connection after attached, call Attach with the same PID and incarnation, and give the position of each channel in resume_from.
  3. The new attached gives a start for each channel. The replay begins there and is followed by live output, continuous within the channel. If start is greater than the position you requested, the bytes in between are no longer in the buffer, and the difference is the number of bytes lost.
  4. Send SendInput with input_offset, the position where this input begins in the process's input. On a resend, the part the process has already accepted is skipped and never written twice.

Input positions:

  • The input_offset in a successful SendInput response is the number of input bytes the process has accepted so far, which is the position of the next input.
  • The input_offset in attached is the value at the moment of resuming.
  • If input_offset in a request is greater than the number of bytes the process has accepted, the request returns failed_precondition (CONFLICT), with ErrorDetail.input_offset set to the accepted count, and nothing is written.

Other rules:

  • If a position in resume_from exceeds the number of bytes the channel has produced, Attach returns invalid_argument, and the existing subscription is unaffected.
  • After the process exits, you can still resume while its result is retained: the missed output is replayed first, and then exited arrives.
  • When the buffers of a process were released early to keep the guest total, start on resume is the position at the time of release.
  • If the credential has expired when you resume, request a new one and then resume.
  • Some client libraries report the connection close that happens when a node switches to a new data-plane process as a protocol error. Treat it as a dropped connection and resume; see Stream interruptions.

Troubleshooting

Default user unavailable

  • Symptom: The Workspace starts normally, but requests that specify no user, or the user the Template declares, return failed_precondition with ErrorDetail.reason set to CONFLICT. They are not retryable, and message names the user and the reason. The desktop is unavailable too, while root still works.
  • Cause: The user the Template declares does not exist in the guest, has a uid or gid of 0, or has no home directory. Usually it was deleted or renamed inside the guest.
  • Resolution: Run as root to restore the user, then restart the Workspace. The default user and the desktop come back with it.

Next steps

  • Files: upload, download, and watch files in the guest.
  • Services and ports: open web services and forward ports.
  • Desktop: view and operate a graphical desktop.
  • API overview: credentials and conventions for your own program.

On this page