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 likeTags('${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
andfalse
.
Operators:
- Comparison operators
==
,!=
,>=
,<=
,>
, and<
for comparing values. - Logical operators
not
,and
, andor
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 returntrue
if the socket has that tag orfalse
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 claimsHeaders(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 valuesQuery(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 ofpath
. Eg.Json('${DATA}', '$.name')
will extract thename
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 usinghttp_header
tokens should be sent in theAuthorization
request header asBearer <token>
. If usingquery_string
, the token should be sent as thetoken
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 iftype
isweb
, 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. Iftype
is'none'
it can also benull
.
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}}
.