Skip to main content

The Kanalo Configuration File

In order to direct data through the Kanalo system, each tenant relies on a configuration file. The Kanalo configuration file is a YAML file (*.kanalo if created locally) that describes what the Kanalo system will do in response to each incoming event, as well as the authentication configuration for websocket clients. To write the configuration for your application, you can use the editor in the dashboard, or write the file locally and upload it to your tenant using the CLI.

The rest of this page describes the structure and fields of the Kanalo configuration file. An example file is shown here, followed by a breakdown of the different parts:

version: '1.0.0'
hooks:
CONNECT:
- name: OnConnection
rule: '*'
func: http://foober.dev/onConnect
commands:
- command: tag
params:
tagsToAdd:
- ${Claims('userType')}

MESSAGE:
- name: UpdateLocation
rule: Tags('driver') or Tags('rider')
func: http://foober.dev/onMessage/location
- name: RideRequested
rule: Tags('rider')
func: http://foober.dev/onMessage/request

DISCONNECT:
- name: DriverDisconnect
rule: Tags('driver')
func: https://foober.dev/onDisconnect/driver/${ID}

clients:
foober:
name: Foober App
type: web
tokenLocation: query_string
wsTimeout: 15000
authConfig:
type: jwt
settings:
claims:
iss: https://foober.<authentication-provider>.com
aud: https://foober.kanalo.com
maxAge: 30d
verifier:
algorithm: HS256
secret: put-secret-here

version

This is a string indicating the version of the Kanalo file schema this configuration file follows.

version: '1.0.0'

hooks

The hooks field describes the response to a given event (ie. one or more triggered "hooks"). This allows automatic routing of events to different Functions(sending data to backend services) and/or executing Pipeline commands(routing data to other websockets), based on a system of rules that you define.

Events

There are three optional keys below hooks: CONNECT, MESSAGE, and DISCONNECT. These hold the hooks for their respective events.

  • CONNECT events happen when a client first connects, and contain the claims of their authentication token, query parameters, and request headers.
  • MESSAGE events happen when a client sends data through the websocket, and contain the data sent.
  • DISCONNECT events happen when the WebSocket connections is closed, and contain the disconnection code and reason.

Hook descriptions

Each event key can contain one or more hooks in a list format. Each hook is an object with an optional rule property, an optional func property, and an optional commands property, all described below, and can be named using the optional name property. An example hook definition:

hooks:
CONNECT:
- name: OnConnectionTag
rule: Query('tagMe')
func: https://example.com/tagged
commands:
- command: tag
params:
tagsToAdd:
- ${Query('tagMe')}

As a quick example, this hook named 'OnConnectionTag' will fire anytime a new client connects with the 'tagMe' query parameter set to some value. It will tag that socket with the value of the 'tagMe' query parameter, and forward all the CONNECT data (claims, query params, and headers) to the url given for func.

name

The name field is a string, and allows for easier debugging.

rule

The rule field describes when the hook will fire, based on the data and/or metadata of the event. If omitted, it is considered to be '*' (catch-all), and so will always trigger when the event occurs. More complex rules can be written as strings using the properties and syntax described below. These should be statements that evaluate to either true or false using a function or comparison operator, or combination thereof using logical operators.

Values:

  • Strings can be written using single or double quotation marks ('myString' or "myString"), and can contain event metadata by using the properties and functions listed below with the syntax ${variable} (so you can do something like Tags('${TenantName}:messages') to see if the socket is tagged with the <myTenant>:messages tag, where <myTenant> is your tenant name).
  • Numbers are written directly eg. 42.13.
  • Booleans are written as true and false.

Operators:

  • Comparison operators ==, !=, >=, <=, >, and < for comparing values.
  • Logical operators not, and, and or for chaining operations.
  • Parenthesis ( and ) for ordering operations.

Properties:

  • ID (string): The socket's id value.
  • IPADDR (string): The IP address of the socket.
  • TenantName (string): The name of the tenant.
  • ClientName (string): The name of the client application the socket connected with.
  • DATA (string): The contents of the data (stringified if not already a string).

Functions:

  • Tags(string): Takes a single tag string as an argument and will return true if the socket has that tag or false otherwise.
  • TagsMatch(...string): Takes one or more glob pattern strings as arguments and will return the first tag matching one of the patterns.
  • TagsRegex(string): Takes an regex string as an argument and will return the first tag matching it.
  • Claims(string): Returns the value of the claim key passed as an argument. Claim values that are objects will be stringified.
  • ClaimsMatch(...string): Takes one or more glob pattern strings as arguments and will return the value of the first key in the claims matching one of the patterns. Will not return non-string claim values.
  • ClaimsRegex(string): Takes an regex string as an argument and will return the value of the first key in the claims that it matches. Will not return non-string claims
  • Headers(string): Returns the value of the header passed as an argument. Header values that are objects will be stringified.
  • HeadersMatch(...string): Takes one or more glob pattern strings as arguments and will return the value of the first header matching one of the patterns. Will not return non-string values.
  • HeadersRegex(string): Takes an regex string as an argument and will return the value of the first header that it matches. Will not return non-string values
  • Query(string): Returns the value of the query string parameter passed as an argument. Values that are objects will be stringified.
  • QueryMatch(...string): Takes one or more glob pattern strings as arguments and will return the value of the first query string parameter matching one of the patterns. Will not return non-string values.
  • QueryRegex(string): Takes an regex string as an argument and will return the value of the first query string parameter that it matches. Will not return non-string values.
  • Json(item, path): Takes a JSON string, parses it, and returns the value of path. Eg. Json('${DATA}', '$.name') will extract the name field from the message data. Path syntax can be found here.
  • MatchTest(string, pattern): Tests if the given string matches the given glob pattern.
  • MatchExtract(string, pattern): Returns the first substring from the given string that matches the given glob pattern.
  • RegexTest(string, regex): Validates if the given string matches the given regex string.
  • RegexExtract(string, regex): Returns the first match when the given regex is executed on the given string.

For functions, prefer to use regular matching over glob matching over regex for simplicity. For basic glob syntax, see here. Regular expressions passed to regex functions must be RE2 compliant.

func

The func field holds a single url string that describes the HTTP endpoint (a Function in Kanalo parlance) where event data is sent. Like strings in rules, these can have variables that access the metadata of the event, using the same ${variable} format. The metadata properties that urls has access to are the same as those in the rules, as described above under Properties and Functions. For example, the following hook description sends the data to different URLs depending on the socket's generated id property:

- name: PassAlongData
func: http://example.com/users/${ID}

This backend endpoint can then respond with Commands if so desired. See Functions for full details.

commands

Commands describe an array of Pipeline commands that are executed by Kanalo when the hook is fired. This offers functionality similar to the Commands that can be sent by a backend service, without leaving Kanalo. This makes them very fast, however, they are limited in that they can only use preset strings and the event metadata for their actions.

The commands field is an array of command objects that take the following form:

command: 'message'
params:
message: Hello ${IPADDR}

The command property holds the string name of the command to be invoked. The params property is an object containing the arguments for the command, which depends on the command invoked. For a full list of available Pipeline commands and their arguments, see Pipelines.

Like urls, Pipeline commands have access to the event metadata for their string arguments, using the same braces syntax as seen in the examples above.

clients

clients is the other main object in the configuration file. It holds definitions of different client applications that can connect, and stores the configuration and authentication information for the websocket connections they make. Each application is keyed with the url path it will use to connect to Kanalo (the default echo client that each tenant starts with would therefore connect to wss://<tenant>.on.kanalo.dev/echo), and is an object with the following properties:

  • name (string): A display name. Not the key/path name mentioned above.
  • type (string): The platform of the client, either 'web' or 'native'.
  • tokenLocation (string): The location of the authentication token, either 'query_string' or 'http_header'. If using http_header tokens should be sent in the Authorization request header as Bearer <token>. If using query_string, the token should be sent as the token query parameter: wss://<tenant>.servcie.kanalo.dev/<client>?token=<token>.
  • wsTimeout (number): If a socket loses contact without a formal close, the number of milliseconds before it is considered to have timed out.
  • allowedOrigins (string[]): Required if type is web, not used otherwise; a whitelist of url origins that are allowed to create connections.
  • authConfig (object): See shape below.

authConfig

The authConfig object has two properties:

  • type (string): The type the config takes, either 'jwt' for JSON web token format, 'static' for a static key (the starting default for new tenants), or 'none' for no authentication (do not use 'none' outside of internal testing, and ideally, not at all).
  • settings (object): See shape below. If type is 'none' it can also be null.

The settings object is further nested and has multiple options, so for clarity their TypeScript interfaces are shown here before their descriptions:

Settings

interface ISettings {
claims: {
iss: string;
aud: string;
};
maxAge: string;
verifier: IJWTAsymmetricKeyParams | IJWTSymmetricKeyParams;
}

interface IJWTSymmetricKeyParams {
algorithm: 'HS256';
secret: string;
}

interface IJWTAsymmetricKeyParams {
algorithm: 'RS256' | 'ES256';
type: 'jwk' | 'key';
value: IJWTAsymmetricJSONWebKey | IJWTAsymmetricKey;
}

interface IJWTAsymmetricJSONWebKey {
url: string;
}

interface IJWTAsymmetricKey {
pubKey: string;
}
  • claims: Holds the issuer and audience for the authentication token.
  • maxAge: The maximum token duration.
  • verifier: Holds an object containing information on the signing algorithm as described below.

If using a symmetric key, the algorithm must always be 'HS256', and the secret is the user-provided string for signing the token.

If using an asymmetric key, there are two choices of algorithm: 'RS256' or 'ES256'. The type field gives a choice of how the key material will be acquired. Each of these corresponds with an option for the value field, either an object with the url field indicating the location of the JSON Web Key for 'jwk', or an object with the pubKey field with the key material in .pem form.

Environment Variables

Kanalo configuration files can use environment variables to further customize deployments or hold secrets. Environment variables for each tenant can be edited on the Environment Variables section of the dashboard. Each variable has a name/key, to refer to it in the config file, and a value, which will replace the reference when the config file is loaded.

Once defined in the tenant, variables can be used in the configuration file by using double brace notation around their name: {{VARIABLE_NAME}}.