Write operations and errors
Every program that changes state through the API follows the same rules: query the server's capabilities first, carry a command ID and an expected revision on write requests, and read errors in one format. This page describes each rule and the fields that carry it.
Querying capabilities first
Before calling business methods, call ServerInfoService/GetServerInfo. The response has two fields:
| Field | Meaning |
|---|---|
source_revision | The source revision the server was built from, for display only |
capabilities | A list of strings such as workspace.create, workspace.filesystem, and workspace.desktop. For the full list and naming rules, see Capability names |
- There is no API version number to compare.
- When an operation needs a capability that is not in the list, fail that one operation instead of rejecting the whole server.
- Before each command,
kibomatches the exact string and exits with code14when it is missing.
Write operation conventions
Two fields make write requests safe to repeat and safe to run concurrently. A command ID lets you resend a request without running it twice. An expected revision keeps a request from overwriting someone else's change.
Command IDs
Except for the two requests listed below, every write request carries a client-generated command ID:
| Surface | Field location | Format |
|---|---|---|
| Workspace API | CommandMetadata metadata in the request, field command_id | ^[A-Za-z0-9][A-Za-z0-9._/-]*$, 1 to 128 bytes |
| Operator API | string command_id in the request | Same as above |
v0.3.x still accepts a command ID that does not match this format. From v0.4.0 it returns a non-retryable invalid_argument. For how deployers find such callers, see Upgrade from v0.2.x to v0.3.0.
These IDs have further limits:
| ID | Limit | If it does not comply |
|---|---|---|
| All write requests | Cannot start with expiration/, host-memory/, or guest-shutdown/. These prefixes are reserved for commands ctrl starts itself | The Workspace API returns invalid_argument now. The Operator API does from v0.4.0 |
command_id of ApplyArtifactReclamation | Cannot contain / | invalid_argument |
command_id of Template and Platform publications in BeginArtifactPublication | Matches ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$: it cannot contain / and can contain : | BeginArtifactPublication itself succeeds, and the publication fails when it writes the manifest |
GetWorkspaceCommandandGetVolumeCommandread existing commands, so these rules do not apply to their IDs.kibotos.ValidateCommandIDin the Go SDK checks the format and reserved prefixes above. Your program must call it; the SDK does not check when it sends a request.kibochecks--command-idbefore it sends the request.
The command ID appears only in the request message; it is not an HTTP header. Save it before you submit.
The server compares the ID and the input with what it already has:
- Submitting again with the same command ID and the same input returns the first result and does not run the command again. Failures replay too: after a command fails, the same ID returns only that failure.
- A command ID stands for one attempt. While the result is unknown or the command is still running, keep the ID. When the error details have
command_settled, the failure is final, so a new attempt takes a new ID. - When you derive a command ID from an object, for example to avoid creating it twice, include something that changes between attempts, such as the failure count. Otherwise the command can never run again after one failure.
- Submitting with the same command ID but a different input returns
already_exists. CreateWorkspaceandUpdateWorkspaceNetworkPolicyignorenetwork_policy.revisionwhen comparing inputs. The server assigns this value, and whatever the request contains is ignored.
RevokeEnrollment and RetainLaunchRevision have no command ID because their effects can be applied repeatedly. Revoking again does not change the result, and retaining again until the same time does not either.
When a command returns
When a command returns depends on the command:
| Command | Returns | How to get the result |
|---|---|---|
CreateWorkspace, StartWorkspace | As soon as accepted. The Workspace is CREATING or STARTING, and the executor keeps working | GetWorkspaceCommand |
StopWorkspace, DeleteWorkspace, RevokeWorkspaceDataPlane | When the command completes, waiting at most 1 minute | A timeout returns a retryable UNAVAILABLE and the command keeps running. Query with GetWorkspaceCommand |
UpdateWorkspaceNetworkPolicy | On an AVAILABLE Workspace, when the node has confirmed or failed the change, waiting at most 1 minute. On a STOPPED Workspace, at once | A timeout or an unreachable node returns a retryable TIMED_OUT or UNAVAILABLE, and the Workspace is UNKNOWN until the node reports again. See When changes take effect |
ExportWorkspace, ImportTransfer, RestoreWorkspace | When the command completes, waiting at most 1 minute | Timeouts work the same way. The error details carry the current Transfer; call GetTransfer with its transfer_id |
Volume create, resize, and delete, ResizeWorkspaceWritableLayer | Completes within the request; the response carries the resulting state | When the result is unknown, use GetVolumeCommand for Volume commands and GetWorkspaceCommand for writable layer resizes |
Important
On a timeout or an unknown result, you can also resend with the original command ID. Do not switch to a new one.
Expected revisions
Requests that modify an existing object carry an expected revision. When the object's current revision does not match, the server rejects the request so it does not overwrite someone else's change.
Requests of the Workspace API:
| Request | Expected revision field | Object revision it matches |
|---|---|---|
UpdateWorkspaceExpiration | expected_revision | expiration.revision |
StopWorkspace, StartWorkspace, DeleteWorkspace | expected_expiration_revision | expiration.revision. There is a revision number even when no expiration is set |
UpdateWorkspaceMemory, UpdateWorkspaceCpu | expected_revision | The resources revision resources_revision, shared by both |
UpdateWorkspaceVolumeBinding | expected_revision | The Volume binding revision binding_revision, independent of the resources revision |
UpdateWorkspaceNetworkPolicy | expected_revision, optional | network_policy.revision. When it is 0, no check is made. When it is not 0, a mismatched revision is rejected even if the content is the same as the current content |
UpdateEgress, UpdateResolver | expected_revision | The revision of the Egress or Resolver |
Label and annotation updates of Workspaces, Volumes, Egresses, and Resolvers, such as UpdateWorkspaceLabels and UpdateVolumeAnnotations | expected_revision | The object's labels_revision or annotations_revision |
Requests of the Operator API:
| Request | Expected revision field | Object revision it matches |
|---|---|---|
DrainNode, CancelNodeDrain, IsolateNode, RetireNode, SetNodeOvercommit | expected_registration_version | The node's registration_version |
SetOvercommitDefaults | expected_version | The version of the overcommit defaults |
SetStoragePoolCapacity | expected_version | The storage pool's version |
SetPlatformDefault | expected_revision and expected_default_version | The current default revision and its pointer version |
SetTemplateDefault | expected_revision, or expect_no_default | The current default revision |
ResizeWorkspaceWritableLayer and ResizeVolume have no expected revision, only a command ID and a target capacity.
The error for a mismatch depends on the revision:
| Mismatched revision | Error |
|---|---|
The expiration revision: expected_revision of UpdateWorkspaceExpiration, or expected_expiration_revision of stop, start, or delete | failed_precondition |
| Any other expected revision | aborted |
Changing the expiration time also returns failed_precondition when the Workspace state does not allow it. Use observed_workspace in the error details to tell the two cases apart. See Expiration.
A label or annotation update of a deleted object also returns failed_precondition.
After either error, read the object's state again before deciding what to do next. Do not automatically replay the old request with the new revision.
Error format
Errors use the standard Connect error codes. The error details include a kibotos.ctrl.v1.ErrorDetail. Base your handling on these fields:
| Field | How to use it |
|---|---|
reason | A finer-grained reason from the FailureReason enum, such as SESSION_EXPIRED, CAPABILITY_UNAVAILABLE, or CONFLICT. Check it before the standard code |
retryable | Whether trying again may succeed. When it is false, change the request or the object first. command_settled decides which command ID to use |
command_settled | This is the command's recorded result: the same command ID only replays it, so retry with a new ID. A retryable failure without this field has no result yet, so resend with the same ID |
accepted_bytes | Only on the resource_exhausted that SendInput returns: the bytes the guest accepted before refusing |
observed_workspace, observed_volume, observed_transfer | The object as it was when the request failed. Use it to decide the next step, for example to tell a revision conflict from a state that does not allow the change |
For the other fields, such as stage and message, and for every reason, see ErrorDetail and FailureReason in the API reference.
The exit code of kibo is chosen from reason first and the standard error code second. For the exit codes, see kibo exit codes.
Next steps
- Authentication: obtaining and renewing the Operator identity.
- Data plane: session credentials and calls to the node.
- Capability names: every capability and what it covers.
- Errors: what each failure reason means and what to do.
Authentication
Requests to ctrl authenticate with an Operator identity over mTLS, and requests to the node data plane carry a short-lived session credential. This page shows how a program obtains an Operator identity, how to configure TLS for both endpoints, and how to keep the identity renewed.
Data plane
Files, processes, desktops, and service connections go directly to the node that runs the Workspace, without forwarding through ctrl. This page shows how to request a session credential from ctrl, keep it valid, and call the node with it.