WWT KDR Protocol Specification

This document specifies the interactions between the WWT kernel data relay (KDR) and various Jupyter kernels that use it to publish data. The reference kernel implmentation is found in pywwt.

URL Structure

The URLs that are ultimately provided by the KDR have the form:

{base-url}/wwtkdr/{key}/{entry...}

Here, base-url is the base URL of the Jupyter server, which is easily determined on the server side and is not-so-easily determined on the kernel side. The base URL is not necessarily absolute, however, and there may be different kinds of proxies or redirectors in place that prevent either the kernel or the server from knowing the true, actual public URL at which their content is ultimately made available. The only way to reliably construct a fully absolute URL is in the frontend JavaScript code, by combining a KDR URL with the current window’s location, or in a server request handler where the request origin is specified.

The key is a unique identifier associated with exactly one Jupyter kernel. A running kernel must “claim” a key before any data it publishes will be accessible. Later on, a different kernel can override the claim and take over the association between key and kernel; this functionality is required so that URLs have the possibility of continuing to work across kernel restarts. A single kernel may claim multiple keys. Keys starting with an underscore character (_) are reserved and may not be claimed.

The entry... identifies resources published by a specific kernel. The structure and semantics of the entry portion of the URL are unspecified; each kernel may use whichever scheme it deems appropriate.

Claiming Keys

A kernel can claim a URL key by publishing a message of type wwtkdr_claim_key on its IOPub socket. The message content should have the form:

{
  'key': $key:str
}

Where $key is the key being claimed by the kernel. This value should not be empty and it should not be URL-escaped. If the claimed key is illegal (e.g. it starts with an underscore) or the claim message is otherwise invalid, it is ignored by the server.

Requesting Resources

When the server receives an HTTP request for a KDR URL path, the key will be used to map the request to a specific kernel. If the key is unregistered, or if it is associated with a dead kernel, the requestor will receive a 404 error.

On a valid HTTP GET request, the associated kernel will be sent a message on its shell socket of type wwtkdr_resource_request with the following content structure:

{
  'method': 'GET',
  'authenticated': $authenticated:bool,
  'url': $url:str,
  'key': $key:str,
  'entry': $entry:str
}

The $key string provides the key of the request URL. Because a kernel can claim more than one key, it is needed for disambiguation. The key value has been decoded: if the literal URL text is my%2Fkey, the value if $key will be my/key.

The $url string gives the absolute URL of the HTTP request. Due to the way that the Tornado framework works, there are some normalizations that are applied before the KDR can do anything about it: foo/../bar becomes bar, and ./foo becomes foo. But other constructs, such as foo//bar, are not normalized.

The $entry string identifies the resource being requested. The relay is not responsible for, or capable of, checking its validity. For the time being, the value is merely extracted from the URL and relayed to the kernel, including the normalizations described above. Clients should use this value instead of trying to parse anything out of $url.

The $authenticated boolean indicates whether the request is coming from an authenticated client, as determined by the Tornado framework. It is up to the kernel to determine if unauthenticated users should be allowed to access the resources that it publishes.

The kernel should reply on its shell socket with one or more messages of type wwtkdr_resource_reply. While every reply message must contain some baseline JSON content, the first reply message must contain additional fields (analogous to HTTP header data). Every reply message, except for the last one, must also be associated with one or more byte buffers, which contain the resource binary content to be returned to the client that has connected to the server. The final reply message is allowed to arrive without any associated buffers.

The JSON content of the every reply message should contain the following fields:

{
  'status': $status:str,
  'seq': $seq:int,
  'more': $more:bool
}

Furthermore, the JSON content of the first reply message should contain the following additional fields:

{
  'http_status': $httpStatus:int,
  'http_headers': $httpHeaders:list<[str, str]>,
}

The $status field is a Jupyter messaging status indicator. It should be "ok" if the request was processed successfully (even if the resulting HTTP status is an error status). If an error was encountered, the value should be "error", and the structure of the reply should be as described in the jupyter_client documentation. The value of the evalue JSON field, if present, will be relayed to the requestor as part of an HTTP 500 error.

The $seq field gives a sequence number for each reply message, starting at zero and increasing by one with each reply. This is needed because some implementations of the Jupyter messaging protocol don’t guarantee in-order message delivery.

The $more field indicates whether more reply messages will be sent. If your implementation doesn’t “know” when the last reply will be sent until all data have been transferred, send a final message with no associated byte buffers.

On the first reply, the $httpStatus field should give an HTTP status code that will be relayed to the requesting client. The $httpHeaders field should be a list of sub-lists, each sub-list consisting of two strings: an HTTP header name and a header value. These will be included in the HTTP response issued by the relay, and should include fields such as Content-Type.

Support Interfaces

The KDR provides one additional support API. New APIs, or extensions to existing APIs, may be added in the future.

Probe API

A request to the following URL may be used by a frontend to probe whether the KDR server extension is available:

{base-url}/wwtkdr/_probe

If the extension is installed, the following JSON content will be returned:

{
  'status': 'ok'
}

This API is marked as requiring authentication, so it must be accessed from a session that is logged into the current Jupyter session.