Kibotos

Services and ports

You can open a service in a Workspace from a browser or client on your machine, publish it at a public URL for others, or connect your own program to a port or socket inside the guest. This page covers each of these, starting with the kibo commands.

Overview

Choose by who needs to reach the service:

NeedUseWho can reach it
Open a web service in your local browserkibo workspace service httpOnly this machine
Connect a local client such as psql, SSH, or VNCkibo workspace service forwardAny process on this machine
Connect a web page on your machine to a port or socketkibo workspace service bridgePages from one local origin
Show a web service to someone elsekibo exposure createAnyone who has the address
Connect your own program to a port or socketA service connectionOnly the holder of the session credential

Only a public URL needs a domain, which the deployer configures for it.

Opening an HTTP service in a local browser

The kibo workspace service http command proxies an HTTP port in the Workspace to 127.0.0.1 on your machine so you can open it in a browser. Only this machine can reach it, and the deployer does not need to configure a domain.

Proxy port 5173 in the Workspace and open the URL it prints:

kibo workspace service http "$workspace_id" --tcp-port 5173
# URL  http://127.0.0.1:5173/

The proxy runs until you press Ctrl-C. Connections in progress end with it.

You can leave the proxy running. The credential is renewed automatically before it expires, and you do not need to reopen the proxy after the Workspace restarts.

Parameters

ParameterDefaultDescription
--tcp-portRequiredThe port the service listens on in the Workspace
--familyipv4Use ipv6 when the service listens on ::1
--listen-portSame as --tcp-portLocal port. 0 picks a free port

Notes

  • By default, the local port is the same as the port in the Workspace, so callback addresses hard-coded as localhost:5173 and CORS settings in the application keep working.
  • When the local port is in use, the command fails with exit code 2. Use a different --listen-port.
  • A service that listens only on 127.0.0.1 works. You do not need to change it to listen on 0.0.0.0.
  • The service can start after the proxy. Until it starts, requests return 502.
  • The proxy accepts only requests whose Host is 127.0.0.1 or localhost plus the local port. Any other Host returns 403. This blocks web pages that resolve another domain to 127.0.0.1.
  • The application sees the Host the browser sent, so redirects and absolute URLs it generates come back to the local proxy.
  • Keep-alive, streaming responses such as SSE, and WebSocket all work.
  • The proxy holds at most 32 connections to the service at the same time, counting idle, in-use, and WebSocket connections together. A request that finds none free waits for one. A WebSocket holds its connection until it closes, so 32 open WebSockets hold up every other request.

To show the service to someone else, publish a public URL.

Local port forwarding

The kibo workspace service forward command forwards a TCP port or a managed Unix socket in the Workspace to a port on 127.0.0.1 on your machine. It does not interpret the application protocol, so local clients can connect directly to services that do not use HTTP/1.1, such as databases, SSH, VNC, and gRPC.

Forward PostgreSQL on port 5432 and connect to it with psql:

kibo workspace service forward "$workspace_id" --tcp-port 5432
# Address  127.0.0.1:5432
psql -h 127.0.0.1 -p 5432 -U postgres

To connect to the desktop with a native VNC client, forward the desktop's VNC socket to local port 5900:

kibo workspace service forward "$workspace_id" \
  --unix-path /run/kibotos/services/desktop/vnc.sock --listen-port 5900

Forwarding runs until you press Ctrl-C. Connections in progress are reset with it.

Warning

Forwarding does not inspect the data that passes through it. Any process on your machine can connect to the port, including processes of other accounts, and web pages in a browser can send requests to it. For an HTTP service, a web page can read and write it through DNS rebinding, so use service http for HTTP services; it checks Host. The risk is the same as with kubectl port-forward and ssh -L.

Parameters

ParameterDefaultDescription
--tcp-portOne of this or --unix-pathThe port the service listens on in the Workspace
--familyipv4Use ipv6 when the service listens on ::1
--unix-pathOne of this or --tcp-portA Unix socket under /run/kibotos/services/ in the Workspace
--listen-portSame as --tcp-port; required for a Unix socketLocal port. 0 picks a free port

Notes

  • Each local connection maps to one connection in the Workspace. It is established as soon as you connect, so protocols where the server sends first (MySQL, SSH, VNC) do not wait for the client to write.
  • Data in both directions is forwarded unchanged. When one side finishes writing, the other side reads EOF.
  • When one side stops reading, writes from the other side wait. The connection is not closed because of this, and there is no idle timeout.
  • At most 32 connections are forwarded at the same time. New connections beyond that are reset.
  • When the service cannot be reached, the Workspace stops, or a connection fails midway, the local connection is reset and stderr gets a line with the reason, for example connection from 127.0.0.1:52144 reset: unavailable: ….
  • The service can start after forwarding starts.
  • You can leave forwarding running. The credential is renewed automatically before it expires, and you do not need to restart forwarding after the Workspace restarts.
  • When the local port is in use, the command fails with exit code 2. Use a different --listen-port.
  • A Unix socket must be owned by root with mode 0600, and the path cannot pass through a symbolic link. Otherwise the connection fails with permission_denied.

Local WebSocket bridge

The kibo workspace service bridge command turns one service in the Workspace into a WebSocket on 127.0.0.1, for a web page on your machine that talks to the service directly. It prints the WebSocket URL, which ends in /stream, and the address of a built-in test page that checks whether an echo service in the guest returns bytes unchanged.

Bridge port 7000 in the Workspace for a page that you serve at http://127.0.0.1:8080:

kibo workspace service bridge "$workspace_id" --tcp-port 7000 --origin http://127.0.0.1:8080
  • The target flags --tcp-port, --family, and --unix-path work as for service forward. --listen-port sets the local port; by default a free port is picked.
  • Only handshakes whose Origin equals --origin are accepted. Without --origin, only the built-in test page can connect.
  • Each WebSocket gets its own connection to the service. Every message is binary and carries the bytes unchanged, with no opcode byte, at most 32 KiB per message. A text message closes the WebSocket.
  • When the service ends its writing side, the bridge ends the connection and closes the WebSocket with 1000. The page cannot send more after that.
  • At most 16 WebSockets are bridged at the same time. Further handshakes return 503.
  • The credential is renewed automatically. The bridge runs until you press Ctrl-C, and open WebSockets close with it.

Public URLs

The kibo exposure command turns an HTTP port inside a Workspace into an https:// address that anyone who has the address can open in a browser. Use it when an Agent writes a web service in a Workspace and you need to see it directly.

Prerequisites

Without that domain, kibo does not list the kibo exposure commands, and creation fails.

Creating a public URL

Publish port 3000 of the Workspace for two hours:

workspace_id=workspace-example
kibo exposure create --workspace "$workspace_id" --port 3000 --ttl 2h

The url in the output is the address you can open directly or send to others. It has no trailing slash; to reach a path, append it directly.

Warning

There is no access control. Anyone with the address can access the service, with no password, origin, or IP restriction. The address is random, and that is its only protection. Do not use it to publish anything that should not be seen.

Listing and revoking

List the Exposures of a Workspace, show one, and revoke it:

kibo exposure list --workspace "$workspace_id"
kibo exposure get exposure-example
kibo exposure revoke exposure-example

After revocation, the address stops serving immediately, and connections in progress end with it. Revoking again is not an error.

Notes

  • The port is read on the guest loopback. A service that listens only on 127.0.0.1 works. You do not need to change it to listen on 0.0.0.0. When the service listens on ::1, add --ipv6.
  • After an Exposure is revoked, its address is never given to anyone else.
  • --ttl is required, with a maximum of 24 hours. Serving stops automatically at expiry.
  • When the Workspace stops or is deleted, all its Exposures are revoked with it. After the Workspace starts again, create a new Exposure; it gets a new address.
  • The only protocol is HTTP, including WebSocket and SSE. TCP, TLS passthrough, UDP, and QUIC are out of scope.

Exposure API

Programs manage public URLs through ExposureService: CreateExposure, GetExposure, ListExposures, and RevokeExposure. The server advertises the exposure.* capabilities only when the deployer has configured the dedicated domain. See Capability names.

When what you need is a byte channel for your own program rather than an address for a browser, use a service connection. It needs a credential, does not need a domain, and is not open to the internet.

With the API

A service connection connects a program to a TCP loopback port or a Unix socket inside the guest, such as VNC, Chrome DevTools, or an application port. The kibo workspace service commands and the desktop viewer page are all built on it.

Requesting a session

The first step is the same for both connection paths. When you request the data-plane session, request the SERVICE_CONNECT scope and list the targets in service_targets. For the session request itself, see Requesting a session credential.

TargetFields
TCPLoopbackTargetaddress_family, port
UnixStreamTargetpath
  • Targets must be exact. Wildcards, port ranges, and arbitrary IPs are not supported.
  • A Unix socket target follows the same rules as --unix-path. See Local port forwarding.
  • The credential is valid only for the listed targets.
  • The target list is part of the idempotency check.

Choosing a path

After that there are two paths with the same semantics and different encodings:

WebSocket (default)Bidirectional stream
EndpointGET /v1/service on session.endpointServiceConnectionService/Connect
RequiresAn ordinary WebSocket upgradeEnd-to-end HTTP/2 on every hop
Suited toDeployments with an L7 reverse proxy or CDN in front; browsersDirect deployments or deployments behind an L4 proxy

WebSocket

Connect to a TCP port or to a Unix socket in the guest with one of these requests. The full list of parameters is in /v1/service.

GET <endpoint>/v1/service?tcp_family=ipv4&tcp_port=9222
GET <endpoint>/v1/service?unix_path=/run/kibotos/services/desktop/vnc.sock

Pass the credential in a header:

  • Put the credential in Authorization: Bearer.
  • Browsers cannot set request headers on a WebSocket handshake, so use Sec-WebSocket-Protocol: kibotos-dp-credential.<credential> instead. The server selects the same subprotocol in the 101 response.

Warning

Do not put the credential in the query. It ends up in access logs and the Referer.

Authentication and the connection to the guest both complete before the upgrade, so a 101 response means the socket inside the guest is already connected. Failures before that are ordinary HTTP responses, with the same error body as /v1/files.

After the upgrade, bytes travel in binary frames whose first byte is an opcode, and failures arrive as close frames. For the opcodes, see Frames; for the close codes, see Close codes.

Warning

Only close code 1000 means the connection completed normally. 1001 is a failure here, although many WebSocket libraries treat it as a normal end: for example, when the service in the guest resets the connection, the server closes with 1001 and a reason starting with unavailable. Check the close code yourself.

Bidirectional streams

  1. Open ServiceConnectionService/Connect to session.endpoint. This is a Connect bidirectional stream over HTTP/2.
  2. Send open{target} as the first message. The target must be in the list granted to the session. After the server connects to the socket, it replies with one connected message, and only then does data start to flow.
  3. Send and receive raw bytes in data messages in both directions. Send half_close to stop writing; you cannot send request messages after that. Receiving eof means the peer has closed writing.

After both directions end, the RPC completes normally.

Connection limits

  • A Workspace has at most 128 open service connections, and a node at most 1024, counting both paths together. Beyond that, a new connection fails with resource_exhausted.
  • Both paths share the message size, dial timeout, and rate limit, and end the same way. See Limits and Notes of /v1/service.

Connecting from a browser

Browsers trust only the system root certificates, while data-plane certificates are issued by an internal CA. A browser can connect directly only when a publicly trusted certificate sits in front of the data plane.

Without such an edge, use kibo workspace service bridge or the desktop viewer page. They turn the connection into a WebSocket on the local loopback. When the service in the guest is itself HTTP, use kibo workspace service http to proxy it directly on the local loopback; see Opening an HTTP service in a local browser.

Next steps

On this page