Documentation

v 0.2.2
Please note that things may change during the beta. We try not to break backwards compatibility, but you should follow us on Twitter to keep up with the latest updates.

Introduction

Hydna is a hosted platform designed to trivialize immediate, bi-directional transmission of data between clients. The platform is programming language agnostic and can operate in heterogeneous client environments.

We'll put that mouthful of technical marketing speak into context in this chapter, so aptly named 'Introduction'.

Background

HTTP is stateless. Each request for content is treated as an independent action. When you enter a URL in your browser, a connection to the web server is established; a piece of content is requested, fetched, and displayed; finally the connection is closed. When you click on a link, the browser goes through the entire process again and a new page is displayed (a "page reload").

It is sometimes desireable to keep interacting with a server without reloading the page or navigating to a new location. Ajax partially solves this problem. It allows you to make asynchronous requests — to send requests to the server without having to reload the page.

But Ajax only works on actions that originate from the client (when you actively click on a link, poll by interval etc). What if you want to update a page when another client performs an action, when the event originates from a server, or when some event is triggered on another device?

The WebSocket protocol is the next evolutionary step towards immediate, stateful applications. WebSockets allow you to send data both ways: from the backend to the client, and from the client to the backend.

WebSockets are great. But using WebSockets requires know-how, another server to manage is introduced, and not on all devices and configurations are compatible (does not work in the most popular version of Internet Explorer, Android-based devices, older browsers etc). WebSockets are also complicated to scale compared to the stateless HTTP requests.

What

Hydna is a hosted platform that exposes multiple protocols — or transports, as we refer to them — to maximize compatibility. Among these transports you'll find WebSockets.

This allows you to use Hydna, more or less, like you would WebSockets. But you're not limited to doing so only with devices that speak WebSocket. And you're not required to set up, manage and monitor any servers.

This powerful concept allows you to instantly communicate client to client, client to server, server to client, and server to server. Where both "client" and "server" are kind of vague terms that can be used to mean anything from a mobile device or latop, to a rack-server running an obsolete unix-like OS or a dumb terminal issuing curl-commands.

Excluding client libraries, Hydna consists of three parts: transports, routing, and Behaviors:

Transports

Transports are the different protocols clients can use to communicate with Hydna. The following transports are currently supported, each with it's individual traits and foibles:

  • Binary/raw TCP — the most efficient transport; used in all native implementations (Java, Node.js, Erlang, C++ etc)
  • WebSockets — very efficient; not supported by all browsers and devices; the preferred transport for use in browsers
  • Flash — very efficient, but requires Flash; one of the fallback-transports for browsers that do not yet support WebSockets
  • Comet — another fallback-transport; slightly less efficient, but still very fast; compatible with almost anything
  • HTTP/Push API — limited to sending messages and emitting signals; very easy to use without a client library. Also suitable for one-off messages as it does not require a persistent connection.

The regular developer needn't be overly concerned with transports as client libraries will automatically detect and select the best available transport for you.

Routing

Routing is the act of accepting data from a sender and delivering it to the intended recipients. Routing works across transports, meaning that data sent from the binary interface (a Node.js server or iOS app, for example) will be automatically converted and sent to all other clients, regardless of the transport they used to connect to Hydna.

Behaviors

Behaviors are small snippets of JavaScript that you deploy to Hydna's infrastructure. They are used to instruct Hydna how to behave when clients perform different actions.

A Behavior can do all sorts of useful things, such as make HTTP requests to external services to verify data or trigger external events, authenticate users, keep track of a global state, permit only certain clients to write to a channel, while allowing other clients receive messages, cache data, etc.

Quick start

Text isn't always the best medium to communicate concepts. The code below illustrates the most basic of implementations — a client that opens a channel, then sends a message, and displays it in an alert-box as soon as it is received.

We've written our examples in JavaScript (browser context), but all concepts should be easily transferable to other programming languages.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hydna example</title>
    <script type="text/javascript"
            src="http://cdnjs.cloudflare.com/ajax/libs/hydna/1.0.0/hydna.js"></script>
    <script type="text/javascript">
      // first we open a channel on the domain `public.hydna.net` in read-
      // and write mode.
      var channel = new HydnaChannel('public.hydna.net/76f3/hello-world', 'rw');

      // then register an event handler that alerts the data-part of messages 
      // as they are received. 
      channel.onmessage = function(event) {
        alert(event.data);
      };

      // finally we add an event handler that sends a message as soon as
      // the channel has been opened.
      channel.onopen = function() {
        channel.send('Hello there!');
      };
    </script>
  </head>
  <body></body>
</html>

If you write the code above to a file and open it in a browser, it should, as soon as a connection has been established and the channel has been opened, display an alert-box containing the text Hello there!.

Any other message sent to the same channel (e.g. by opening the same page in another browser or sending a message to the same channel from another device) will be alert'ed instantly.

Try opening the page you just created in another browser window, or send a message from the terminal using the following curl command:

curl --data "Hello from curl" http://public.hydna.net/76f3/hello-world

This is how the aforementioned concepts correspond to events in the example above:

  • When you load the page, the JavaScript client library asks the browser a series of questions to determine the best available transport. In a modern browser, the client library would settle on WebSockets, while Comet might've been chosen on an Android phone. Also, if you sent data using curl, the HTTP/Push-transport was used.
  • When messages were sent — either when the channel was opened, or if you used the HTTP/Push-transport — they were routed to the intended receiver(s).
  • Behaviors determined if you were allowed to open the channel in read- and write mode or not. This was kind of implicit as the default is to always allow clients to open channels.

You can keep learning by example in the section called 'Tutorials'.

Using Hydna

Using Hydna is as simple as adding a few lines of code to your project, and deciding how to react when data arrives. But, before you start, you need two things:

  1. The first thing is a domain. After you've signed up you can create what is referred to as a domain. Domains are regular domain names (typically *.hydna.net) that are used to identify your own isolated instance(s) within Hydna.
  2. The second thing you need is a client library. There are client libraries for many popular programming languages and we release new client libraries as we detect need. Worth noting is that you can send data over Hydna without a client library using the HTTP/Push transport.

We'll continue using the JavaScript client library in the examples, but want to reiterate that there are many different client libraries.

We'll be covering many parts of the JavaScript client library as we explain the basic actions of Hydna in this chapter. Please refer to the documentation of the client library for a full reference.

You can either download the JavaScript client library or source it directly from CloudFlare:

<script type="text/javascript"
        src="http://cdnjs.cloudflare.com/ajax/libs/hydna/1.0.0/hydna.js"></script>

The script introduces a single class: HydnaChannel that we'll use throughout this chapter.

Opening channels

Before you can send and receive messages you need to connect to Hydna and open a channel.

The number of channels that can be open simultaneously on a domain is limited by plan. All domains currently support 10 000 simultaneously opened channels. Also note that a channel does not equal a connection. An (theoretical) unlimited amount of channels can be opened using the same connection.

Hydna URIs

Instructions on how to open specific channels in Hydna are passed as URIs following the format:

[http[s]://]<domain>[:port][/channel][?token]

Trailing slashes are significant: http://public.hydna.net/hello is not considered the same channel as http://public.hydna.net/hello/.

Channels

Channels are paths used to uniquely identify logical links between clients. You can think of channels much like their radio counterparts: programs transmitted on a channel are received by listeners tuned in to the same channel. Messages sent to the channel /chat are received by clients that have opened /chat in read-mode, but not by clients have opened the channel /stats. You can have multiple channels open on the same domain at any given time.

Channels are opened by creating an instance of HydnaChannel(url, mode):

var channel = new HydnaChannel('public.hydna.net/5e87', 'rw');

Channels in Hydna are (by default) multiplexed: the first time you instansiate a HydnaChannel, you're automatically connected to Hydna; future channel-instances (on the same domain) will re-use the previously established connection. The connection is automatically closed when all channels have been closed.

When a channel has been successfully opened, the onopen-event is triggered and the attached handler is called with the argument event (Object) that contains an optional message event.data.

channel.onopen = function(event) {
    console.log("The channel is now open!");
};

Requests to open channels are sent to Behaviors for processing, allowing you to control who is allowed to do what on different channels.

Modes

Channels can be opened in three major modes depending on what clients plan — or are allowed to, as dictated by Behaviors — to do over them:

  • read mode implies the ability to read messages sent over the channel.
  • write mode implies the ability to write messages to the channel.
  • emit mode implies the ability to emit signals over the channel.

Generally only the first letter of a mode is used when it is referenced: r denotes read, w denotes write and e denotes emit. These shorthand-modes can be combined: a channel can be opened in rw or rwe mode(s), for example.

// opening a few channels in different modes
new HydnaChannel('public.hydna.net/441e', 'r');
new HydnaChannel('public.hydna.net/7088', 'w');
new HydnaChannel('public.hydna.net/427b', 'rw');
new HydnaChannel('public.hydna.net/1cb3', 'rwe');
Valid mode-combinations
Mode (shorthand)Can
read?
Can
write?
Emit
signals?
Receive
signals?
ryesnonoyes
wnoyesnoyes
enonoyesyes
rwyesyesnoyes
reyesnoyesyes
wenoyesyesyes
rweyesyesyesyes

Open channels will always receive signals, regardless of mode.

Tokens

You can also supply a token when opening a channel. A token is a variable length string that is passed to Behaviors and can be used to pass along extra data with the open-request.

var channel = new HydnaChannel('public.hydna.net/71eb?my-password', 'rw');

Dealing with errors

If a request to open a channel is denied, if the channel is later closed, or if something goes wrong, the channel will be closed and a onclose-event is triggered. The event handler takes a single argument — event — with the following properties:

  • reason (String) the reason the channel was closed.
  • hadError (boolean) true if the channel was closed due to an error.
  • wasDenied (boolean) true if the request to open the channel was denied.
  • wasClean (boolean) true if the channel was cleanly ended (either by calling channel.close() or from a Behavior).
channel.onclose = function(event) {
    console.log("Channel closed: " + event.reason);
};

Sending and receiving messages

We commonly refer to data sent through Hydna as messages. A message sent to a channel is immediately routed to all other clients that have opened the same channel in read-mode.

You send messages on channels opened in write-mode with channel.send(message, [priority=0]). The message (String or ArrayBuffer) must not exeed 65 528 bytes in size.

The optional priority (Integer, default 0) allows you to specify the priority of the message. Messages with priority levels other than 0 may be dropped if a client cannot receive messages at the rate they are coming in.

channel.onopen = function(event) {
    channel.send('hello world!');
};

The event handlers attached to onmessage will be called when data arrives. event.data (String or ArrayBuffer) contains the actual message.

channel.onmessage = function(event) {
    console.log('message received: ' + event.data);
};

Emitting and receiving signals

Signals are similar to messages, but are sent to Behaviors instead of being routed to other clients listening for data on a channel.

This is a very useful action when you want to verify messages on the back-end before sending anything to other clients, or if you want to access data stored in Hydna.

channel.onopen = function(event) {
    channel.emit('logged_in');
};

Signals can also be emitted from Behaviors. When a client receives a signal it is guaranteed that it was emitted from your Behaviors — a trusted source.

channel.onsignal = function(event) {
    alert('signal received: ' + event.data);
}

To recap:

  • When a client emits a signal, it's received only by Behaviors listening for emits on the channel on which the signal was emitted.
  • When a Behavior emits a signal, the clients that receive the signal know that it was emitted from a Behavior.

Closing channels

Channels can be closed when they're no longer needed. An optional message can be sent to Behaviors when a channel is closed.

channel.onopen = function(event) {
    channel.close('bye!');
}

Client libraries

Hydna currently supports access through the following client libraries:

Client library Source code and docs Download
Android GitHub Download
ActionScript GitHub Download
C++ GitHub Download
Erlang GitHub Download
Go (Push only) GitHub Download
Java GitHub Download
JavaScript GitHub Download
Julia (Push only) GitHub Download
Lua (Push only) GitHub Download
.NET GitHub Download
Node.js GitHub Download
Objective-C GitHub Download
PHP (Push only) GitHub Download
Python (Push only) GitHub Download
Ruby (Push only) GitHub Download

Plugins and other resources

Name Source code and docs Download
Facebook connect GitHub Download
Unity plugin for Hydna GitHub Download

Push API

The Push API allows you to send messages and emit signals to channels without using a client library. It can also be used to build simple client libraries when you're only interested in sending data.

Hydna exposes a straightforward API over HTTP. It uses a single resource; sending messages is done by making POST requests to the same URI you'd use to connect to Hydna in any client library:

curl --data "Hello World" http://public.hydna.net/1122556827/

Emitting a signal requires a custom header:

curl -H "X-Emit: yes" \
     --data "Hello World" http://public.hydna.net/1122556827/

The API will open a channel, perform the desired action, and disconnect. You can pass a token (that can be accessed in a open-directive in your behaviors) in the URI, as you would when opening a channel in a client library:

curl --data "Hello World" http://public.hydna.net/1122556827/?my_token

You can specify that you're sending binary content by setting the content-type header to application/octetstream (which is ignored when emitting messages):

curl --data @binary_file.png \
     -H "Content-type: application/octetstream" \
     http://public.hydna.net/1122556827

The API returns an appropriate HTTP status code for each request. The code 200 is returned on a successful request.

Behaviors

Behaviors are small snippets of code (JavaScript) that instruct Hydna how to behave when actions (client requesting to open a channel, client emitting a signal to a channel, and client leaving a channel) take place.

This simple, yet very useful, concept can be used to, among other things:

  • Authenticate and dictate what a client is allowed to do (read, write etc) on a channel.
  • Communicate with external services over HTTP
  • Maintain a global in-memory state
  • Emit signals to entire domains, channels, or specific clients.

Bemachines

Behaviors are deployed to sand boxed environments hosted on Hydna's servers. We call these environments Bemachines. Each domain has one or more Bemachine(s) running in parallel (we're currently experimenting with the number of Bemachines per plan; free domains currently have four Bemachines).

It's important to note that Behaviors are inherently stateless as the code is distributed over several Bemachines. Avoid placing code outside the main event-handlers to minimize confusion.

var i = 0;
behavior('/', {
    open: function(event) {
        // the line below will always emit "Client: 0"
        event.channel.emit('Client: ' + i++);
    }
});

(The correct way to of maintain shared state is described later in this document.)

Deploying Behaviors

You can Log in to your account, navigate to the control panel of the domain for which you'd like to create behaviors, and write and deploy code in the online editor.

Event handlers

There are three events you can attach handlers to: open, close, and emit.

Handlers are always associated with the path of the channel the event is triggered on. This allows you to attach different handlers to the channels / and /admin, for example.

Attaching handlers to events

Handlers are attached to events using behavior(path, handlers).

  • path (String) is the path of the channel on which the action is taking place. If a client attempts to open http://public.hydna.net/test, an open-event will be triggered on the path /test.
  • handlers (Object) contains a mapping between event and handler function ({ open: handlerFunction } to attach handlerFunction to the open-event, for example). Valid keys are: open, emit, and close. The value is always a function that takes one argument: event.
behavior('/', {
    open: function(event) {
        // do things when channel '/' is opened.
    },
    close: function(event) {
        // do things when channel '/' is closed.
    },
    emit: function(event) {
        // do things when clients emit signals to '/'.
    }
});

You can attach multiple handlers to a channel/path, but be advised that handlers attached to open-events that call event.allow() or event.deny() will terminate the chain.

Registering named handlers

You can, for convenience, register named handler-objects and load them by reference later.

register('welcome-plugin', {
    open: function(event) {
        event.channel.emit("A client joined: " + event.channel.path);
    },
    close: function(event) {
        event.channel.emit("A client left: " + event.channel.path);
    }
});

// register the event-handlers from welcome-plugin with aboce the path /chat
behavior('/chat', 'welcome-plugin');
behavior('/private-chat', 'welcome-plugin');

Note that named handlers must be registered before use.

Extracting parameters from the path

Bits of the path wrapped in curly braces ({}) are considered variable and can be captured. If you register a handler with the path /rooms/{id}, the handler will be triggered on all events on channels that match the pattern (/rooms/a, /rooms/123 etc), and the "wildcard"-part of the path will be captured and stored in event.params associated with a key matching the characters wrapped in braces.

The code below will allow opening of a channel with the message "You're on room: 1" if a client opens http://public.hydna.net/rooms/1 and "You're on room abcd" if http://public.hydna.net/rooms/abcd is opened.

behavior('/rooms/{id}', {
    open: function(event) {
        event.allow("You're on room: " + event.params.id);
    }
});

Open event handler

The open-event is triggered when a client attempts to open a channel matching the pattern the handler is attached to.

behavior('/admin', {
    open: function(event) {
        if (event.token != 'password') {
            // we've verified that the token supplied when opening the
            // channel matches the password.
            event.deny("You're not allowed to open this channel!");

            // calling event.deny() or event.allow() exits the handler
            console.log("This will never run ...");
        }
    }
});

The event-object has the following methods and properties:

  • event.allow([message]) allow the request to open current channel with an optional message. Calling event.allow() exits the event-handler.
  • event.deny([message]) deny the request to open current channel with an optional message. Calling event.deny() exits the event-handler.
  • event.token (String) the token that was sent with the request to open the channel.
  • event.mode (String) the mode in which the channel requested to be opened ('rw', or 'w' for example).
  • event.read (bool) dictates whether the client intends to read from the channel or not.
  • event.write (bool) dictates whether the client intends to write to the channel or not.
  • event.emit (bool) dictates whether the client intends to emit signals to the channel or not.
  • event.params (Object) object containing params from matching patterns in the registered path.
  • event.connection (Connection) object containing properties and methods of the connection that has triggered the event handler.
  • event.channel (Channel) object containing properties and methods of the channel on which the event handler has been triggered.
  • event.domain (Domain) object containing properties and methods of the domain.

Close

The close-event is triggered when a client closes a channel matching the pattern the handler is attached to.

Close-events will only be triggered on channels that were successfully opened. If you call event.deny() in on an open-event, the close-event will not be triggered.

behavior('/users', {
    close: function(event) {
        // let other clients on the channel know that a client left the channel
        event.channel.emit(event.connection.id + " left " + event.channel.path);
    }
});

The event-object has the following methods and properties:

  • event.data (String) optional reason for closing the channel.
  • event.params (Object) object containing params from matching patterns in the registered path.
  • event.connection (Connection) object containing properties and methods of the connection that has triggered the event handler.
  • event.channel (Channel) object containing properties and methods of the channel on which the event handler has been triggered.
  • event.domain (Domain) object containing properties and methods of the domain.

Emit

The emit-event is triggered when a signal is emitted to a channel matching the pattern the handler is attached to.

behavior('/users', {
    emit: function(event) {
        if (event.data == 'who') {
            event.channel.get('users', function(err, value) {
                if (!err && value != null) {
                    var users = JSON.stringify({ users: value });
                    event.channel.emit(users, event.connection);
                }
            });
        }
    }
});

The event-object has the following methods and properties:

  • event.data (String) the message emitted to the bemachine.
  • event.params (Object) object containing params from matching patterns in the registered path.
  • event.connection (Connection) object containing properties and methods of the connection that has triggered the event handler.
  • event.channel (Channel) object containing properties and methods of the channel on which the event handler has been triggered.
  • event.domain (Domain) object containing properties and methods of the domain.

Making HTTP requests

http.get(url, [callback])

Make a GET-request to url. The optional callback takes two arguments: err and body.

behavior('/', {
    open: function(event) {
        http.get('http://httpbin.org/status/200', function(err, body) {
            if (!err) {
                event.allow("You're clear!");
            } else {
                event.deny("Could not verify request.");
            }
        });
    }
});

http.post(url, data, [callback])

Make a POST-request to url. The optional callback takes two arguments: err and body.

Logging

console.log(message)

Log a message (String) that will appear in the console of your control panel.

behavior('/', {
    open: function(event) {
        console.log("Token was: " + event.token);
    }
});

Note that statistics and log messages are sent from domains on fixed intervals. It might take a few seconds for a log message to show up.

Caching data

Another feature of Behaviors is that you can cache a limited amount of data on channels, connections, and your domain.

Caching is a temporary means of storing data. It is temporary in the sense that it resides in the memory of your bemachines and is never written to disk. How long the data is kept in memory depends on the event handler object it's been stored on:

  • Memory allocated for values stored on a connection ik automatically reclaimed when a client disconnects, when you manually reset your state (from your control panel), or when a domain is restarted (maintenance for example).
  • Memory of values stored on a channel is reclaimed when you manually reset your state, or when a domain is restarted.
  • Memory of values stored on a domain is only reclaimed when you manually reset your cache, or when a domain is restarted.

The amount of memory available varies depending on plan.

Caches of different objects are isolated. A key on domain is not related to any key on a channel. A key on channel /a is not related to the same key on /b. Keys on a connection will only be accessible from events that originate from that specific connection. And so on.

The objects that support caching share the same API. <target> is used below to represent either a domain, a channel, or a connection.

<target>.set(key, value, [callback])

Set key to hold string representation of value.

The optional callback takes one argument: err.

  • err will be set if the memory limit has been reached.
behavior('/', {
    open: function(event) {
        event.connection.set('token', event.token);
    }
});

<target>.get(key, [callback])

Get value corresponding to key.

The optional callback takes two arguments: err and value.

  • value contains the value retrieved. Will be set to null if there is no value associated with the key.
  • err will be set if the memory limit has been reached.

All plans are currently limited to a cache of 5 MB (both keys and values are included in the calculation). We're experimenting with this number.

behavior('/', {
    close: function(event) {
        event.connection.get('token', function(err, value) {
            if (!err && value != null) {
                console.log("Your token was: " + value);
            }
        });
    }
});

<target>.incr(key, [max], [callback])

Increase value associated with key bye one. The initial value is set to 0 if the key does not exist.

The optional callback takes two arguments: err and value.

  • value contains the incremented value (if no errors were triggered).
  • err will be set if the incrementor exceeds max, if the key is not an integer (or coercable to one), or if the memory limit has been reached.

<target>.decr(key, [min], [callback])

Works like incr() above, but decreases the value by one.

<target>.push(key, value, [callback])

Add value to the tail of list stored at key. An empty list will automatically be created prior to adding if the key does not exist.

The optional callback takes one argument: err.

  • err will be set if a value has already been associated with the key and is not a list (i.e. no elements could be added), or if the memory limit has been reached.
behavior('/', {
    emit: function(event) {
        // adds the emitted message to a list
        event.channel.push('message-log', event.data);
    }
});

<target>.pop(key, [callback])

Remove a value from the tail of list stored at key.

The optional callback takes two arguments: err and value.

  • value is the value that was removed from the list. Will be set to null if there are no items left to pop.
  • err will be set if list associated with the key is empty, or if it is not a list.
behavior('/', {
    open: function(event) {
        event.channel.pop('message-log', function(err, value) {
            if (!err) {
                // do something with value
            }
        });
    }
});

<target>.range(key, start, [length], [callback])

Return a range of elements in list at key starting at start (inclusive, zero-based). If start is a negative number the range will start that many elements from the end of the list.

The length-argument dictates the number of elements to retrieve.

  • If length is a positive number range retrieves up to length items from start.
  • If length exceeds the number of elements available, the tail of the list is returned.
  • If length is a negative number, the range will stop that many elements from the end of the list (exclusive — does not include the element at the length-position).
  • If no length is given, the tail of the list is returned.

The optional callback takes two arguments: err and items.

  • items is a list containing the elements in the specified range.
  • err will be set if the key does not exist, or if the value associated with the key is not a list.
behavior('/', {
    emit: function(event) {
        if (event.data == 'get-last-10-messages') {
            event.channel.range('message-log', -10, function(err, items) {
                // only emit if the list has been initialized.
                if (!err) {
                    // emit a JSON object containing the last 10 emits to the client.
                    event.channel.emit(JSON.stringify({
                        type: 'last-10-messages',
                        messages: items
                    }), event.connection);
                }
            });
        }
    }
});

<target>.trim(key, start, [length], [callback])

Works basically the same as range() (see above), but trims the list to contain only elements in the specified range (i.e. it removes all elements not in the range from the list).

The optional callback takes one argument: err.

  • err will be set if the key does not exist, or if the value associated with the key is not a list.
behavior('/', {
    emit: function(event) {
        event.channel.push('message-log', event.data);
        // trim the "message log" to only contain the last 10 added elements
        event.channel.trim('message-log', -10);
    }
});

<target>.del(key)

Delete value associated with key.

behavior('/my-channel', { 
    open: function(event) {
        event.channel.set('users:' + event.connection.id, event.connection.id);
    },
    close: function(event) {
        event.channel.del('users:' + event.connection.id);
    }
});

<target>.findall(pattern, [callback])

Find all keys matching pattern.

Asterisks (*) in the pattern are treated as wildcards that match 0-N unknown characters (lazy).

The optional callback takes two arguments: err and items.

  • err is reserved for future use (and is always null).
  • items is a list of values matching the given pattern.
behavior('/chat', { 
    open: function(event) {
        event.channel.findall('users:*', function(err, items) {
            var msg = JSON.stringify({ users_on_channel: items });
            event.allow(msg);
        });
        event.channel.set('users:' + event.connection.id, event.connection.id);
    },
    close: function(event) {
        event.channel.del('users:' + event.connection.id);
    }
});

Event objects

Connection object (event.connection)

Passed as a property of the event-object to open, close, and emit.

The following properties and methods are available:

Channel object (event.channel)

Passed as a property of the event-object to open, close, and emit, and is returned by domain.getChannel().

The following properties and methods are available:

Domain object (event.domain)

Passed as a property of the event-object to open, close, and emit.

The following properties and methods are available:

Common patterns

Useful snippets, common patterns, and best practices.

Allow all clients to read, but only authorized to write

Commonly used when a privileged user should be able to write, but unprivileged users should still be able to read messages from a channel.

behavior('/admin', { 
    open: function(event) {
        if (event.write) {
            // the client requested to write to the channel. let's examine
            // whether the token matches the secret password.
            if (event.token != 'secret password') {
                event.deny('Not allowed to write to channel.');
            } else {
                event.allow('Allowed to write to channel.');
            }
        } else {
            // the client did not request to write, we can allow that
            // without looking at the token.
            event.allow('Allowed to read from channel.');
        }
    }
});

Limit number of clients on a channel

const MAX = 10;
const KEY = 'clients-in-room';

behavior('/room', {
    open: function(event) {
        // Attempt to increase the number of clients on the channel
        // by one. Will give an error MAX is exceeded.
        event.channel.incr(KEY, MAX, function(err, v) {
            if (err) {
                event.deny("Channel is full");
            }
        });
    },
    close: function(event) {
        // Decrease the number of clients on the channel as clients
        // close the channel.
        event.channel.decr(KEY);
    }
});

Tutorials