Developer API

KasmVNC has an API to make changes or get data via simple HTTPS API calls.

All API calls require owner credentials.

Bottleneck stats

Returns CPU and Network bottleneck statistics. These metrics attempt to identify where performance issues may be occurring. The network and CPU stats each have two numbers. The first number is the current snapshot in time value and the second is averaged over time. Values range from 0 to 10 with 10 representing no constraints. When the frame rate drops, the bottleneck stats should help identify if the bottleneck is server side CPU or the network. The following is an example of what is returned

/api/get_bottleneck_stats

Example Returned JSON

{
    "myusername": {
         "71.61.88.0_1627638421.160028::websocket": [ 8.2, 9.6, 10.0, 9.4 ],
         "71.61.88.0_1627638469.484285::websocket": [ 8.2, 9.6, 2.6, 8.5 ]
    }
}

Because KasmVNC supports multiple users and each user can have multiple connections, the returned JSON contains a dict for each user, which contains an entry for each connection. Each connection has an array with the following contents in order:

CPU, CPU Average, Network, Network Average

In the above example, the second connection for shows that the network is not able to keep up with the frames being sent from the server. It also indicates some CPU constraints, meaning that the CPU is mostly keeping up with the changes to the screen but not at 100%.

Frame Stats

The bottleneck stats are a quick way to determine whether the network or the server-side CPU is causing a bottleneck, however, it does not provide enough fidelity to make coding decisions. The frame stats API call returns timings for all major processes with respect to processing a single frame.

/api/get_frame_stats

client - Which client connection to return frame stats for auto - Return frame stats for the first connection for the owner all - Return frame stats for ALL client connections none - Return no client side statistics 71.61.88.0_1627638421.160028::websocket - Example of providing a specific connection

Example: /api/get_frame_stats?client=auto

Example Returned JSON

{
    "frame" : {
        "resx": 1680,     
        "resy": 971,      
        "changed": 60,    
        "server_time": 45 
    },
    "server_side" : [
        { "process_name": "Analysis", "time": 2 },
	{ "process_name": "Screenshot", "time": 3 },
	{ "process_name": "Encoding_total", "time": 25, "videoscaling": 17 },
	{ "process_name": "TightJPEGEncoder", "time": 3, "count": 1, "area": 786432 },
	{ "process_name": "TightWEBPEncoder", "time": 0, "count": 0, "area": 0 }
    ],
    "client_side" : [
        {
            "client": "71.61.88.0_1627638469.484285::websocket",
            "client_time": 1,
            "ping": 54,
            "processes" : [
                { "process_name": "scanRenderQ", "time": 0 }
            ]
         }
     ]
}

Field

Description

frame.resx

Width of frame

frame.resy

Height of frame

frame.changed

Percent of the frame that had changes

frame.server_time

Wall clock time total to process the frame

server_side.[].process_name

Name of the process

server_side.[].time.

CPU time elapsed total for the process

server_side.[].count.

Total times the process executed

server_side.[].area.

For encoding processes, the total area of rects processed

server_side.[].videoscaling

For encoding_total entry, scaling time in video mode.

client_side.client_time.

Total time for client to process frame

client_side.ping.

Round trip time to client, divide by two

Since encoding in jpeg and webp is multi-threaded, the time specified in the process entries for JPEG and WEBP encoding is CPU time not wall clock time. The “Encoding_total” process logs the wall clock time for all encoding and also includes videoscaling time which only applies when in “video mode”. Video mode scales image down in size, encodes, sends to client, and the client scales it back up. Video mode keeps aspect ratio. Video mode kicks in when screen change reaches a threshold for a period of time defined.

Get Screenshot

Get a screenshot of the current session. You can pass a height and width that do not match the current aspect ratio, it will return the requested width with the height adjusted to keep the aspect ratio.

/api/get_screenshot

width - defaults to actual remote screen width height - defaults to actual remote screen height quality - JPEG quality, defaults to 7 deduplicate - set to true if you want the API call to return nothing if the screenshot has not changed since the last request

Add User

Add another user to the session.

/api/create_user

name - the username password - the requested password write - set to ‘true’ if the user should have write permissions (control of mouse and keyboard) read - Should the user have read access (optional) owner - Should the owner be able to manage users

example: /api/create_user?name=fred&password=123&write=true

Create User can also be called as a POST with JSON data for bulk operations.

[
   { "user": "username1", "password": "password123", "write": true, "read": true, "owner": false },
   { "user": "username2", "password": "password123", "write": true, "read": true, "owner": false }
]

Update a User

/api/update_user

name - The target username password - The password to update (optional) write - Set to true if the user should have write permission (control of mouse and keyboard) (optional) read - Should the user have read access (optional) owner - Should the owner be able to manage users

example: /api/update_user?name=fred&password=123&write=true

Update User can also be called as a POST with JSON data for bulk operations.

[
   { "user": "username1", "write": true, "read": true, "owner": false },
   { "user": "username2", "write": true, "read": true, "owner": false }
]

Remove User

Remove a user from the KasmVNC session

/api/remove_user

name - username of the user to remove

example: /api/remove_user?name=fred

Remove User can also be called as a POST with JSON data for bulk operations.

[
   { "user": "username1" },
   { "user": "username2" }
]

Send Full Frame

Send a full frame to all users with at least read permission.

/api/send_full_frame

Clear Clipboard

Clears the KasmVNC and X session clipboard contents.

/api/clear_clipboard

Unix Relay

KasmVNC provides a method for direct, bidirectional communication between noVNC and containerized applications by utilizing Unix domain datagram sockets.

KasmVNC

To create a Unix relay, KasmVNC should have network.unix_relay.name and network.unix_relay.path configured:

network:
   unix_relay:
      name: relay_name
      path: relay_path

Here, relay_name is the specified name of the relay, acting as an identifier for the connection. The relay_path, on the other hand, represents the path of the Unix domain socket that will be created automatically. This path provides the address at which the socket will be listening, enabling the bidirectional communication essential for the relay to function.

noVNC

In order to connect to a relay on the noVNC side, you can find an example below. This example demonstrates the use of two key functions:

  • rfb.subscribeUnixRelay used to listen to data received from the Unix domain socket,

  • rfb.sendUnixRelayData used to send the data back through the relay.

rfb.subscribeUnixRelay("relay_name", (payload) => {
  const buffer = new Uint8Array(Array.from(payload)).buffer;
  const data = new DataView(buffer);

  // process the incoming data

  // send a response
  rfb.sendUnixRelayData("relay_name", new Uint8Array([...]));
});

Unix Socket Code Examples

In order to connect to a relay on the containerized application side, you can find the example below. This example demonstrates the creation and binding of a socket, and it ensures that the necessary file permissions are in place to provide two-way communication.

import os
import socket
import time

# create and bind socket
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
client_socket_path = f"/tmp/relay-client-{str(time.time())}"
client_socket.bind(client_socket_path)

# allow others to read/write to the socket
os.chmod(client_socket_path, 777)

# send data
server_relay_path = "/tmp/relay"
request = bytearray(1024)
sent = client_socket.sendto(request, 0, server_relay_path)
print(f'sent: {sent}B')

# receive response
response = bytearray(1024)
received, server = client_socket.recvfrom_into(response)
print(f'received: {received}B')

# clean up
client_socket.close()
os.unlink(client_socket_path)

Please note that the created socket file in this example is long-lived. It will remain active as long as the program is running. Care should be taken to manage this resource appropriately in your application, such as cleaning it up when it’s no longer needed by unlinking it after closing.