CRPC is a client-server protocol; Hubot is a client. Servers expose an endpoint listing available methods. Each endpoint provides a regex to fire on and a relative URL path to execute it. CRPC is distilled from several years' experience with Chatops; see the why for some background.
Chatops RPC pushes a lot of complexity to clients. This is a design decision, intended to keep the burden of creating new chat commands in existing systems as low as possible.
A CRPC service listing is an endpoint that exposes JSON including the following fields:
namespace
: A globally unique namespace for these commands. Clients can use this to uniquely identify this endpoint. A namespace should be a slug of the form/[a-Z0-9\-_]+/
.help
: Optional: Overall help for this namespace, if a client chooses to provide helperror_response
: Optional: A message to present when this endpoint returns an error. This can direct users to next steps when the server fails.methods
: A mapping of named operations to their metadata.version
: The version of ChatOps RPC protocol to use, currently version 3
Each key in the methods
hash will be a string name. Each name should be a
slug of the form /[a-Z0-9]\-_]+/
. Clients can use these method names to uniquely
identify methods within a namespace. Each name shall point to an object with the
following fields:
regex
: A string regular expression source used to execute the command. This regular expression should use named capture groups of the form(?<parameter_name>.+)
.path
: A path, relative to the listing URL, to execute the command.params
: A list of available named parameters for this command.help
: Optional: User help for a given command.
Each server is assumed to be given a prefix, which the client will handle
prepending to a command's regex source. Clients can use the namespace
as a
default prefix if they wish, but servers may not demand a particular prefix.
Chatops RPC clients should require whitespace after the prefix, so a command with a
regex like /ping/
with a prefix of test
would match on test ping
.
CRPC clients use the listings to create a listing of available commands. When a chat message matches a command's regex matcher, the CRPC client creates a method invocation. A method invocation is a JSON object with the following fields:
user
: A slug username corresponding to to the command giver's GitHub login.mention_slug
: Optional. If provided, a string which should be used to mention the user when sending a message in response. For example, Slack requires that users be mentioned using user IDs instead of usernames.message_id
: Optional. If provided, an id that uniquely identifies the message that generated the CRPC call. Useful for linking back to the original command to provide context.room_id
: A slug room name where the command originated.method
: The method name, without namespace, of the matching regex.params
: A mapping of parameter names to matches extracted from named capture groups in the command's regex. Parameters that are empty or null should not be passed.
The JSON object is posted to the path
associated with the command from the
listing of commands. CRPC servers should assume that parameters in the params
hash are under user control, but trust that the user
and room_id
to be
correct.
CRPC servers must produce a response JSON object with the following fields:
result
: A string to be displayed in the originating chat room.
CRPC may optionally include the following fields in a response JSON object for
use in situations where richer results can be displayed. Clients will optionally
utilize some or all of the extra information to provide an enhanced response,
but it is important that result
be sufficient on its own.
title
: The title text for the responsetitle_link
: Optional URL to link the title text tocolor
: Hex color for the message, to indicate status/group e.g. "ddeeaa'buttons
: An array of button objectslabel
: The text to display on the buttonimage_url
: An image URL to display as the button, will generally take precedencecommand
: The command to use when the button is clicked
image_url
: An image URL to be included with the responseattachment
: Optional boolean which hints the recipient to format the message as an attachment, if supported by its protocol. Because this is a hint, it may be ignored by clients. If not specified, it defaults to false.
CRPC may also produce error JSON according to the JSON-RPC spec, consisting of
an object containing an error
object with a message
string. This is
sometimes helpful for clients that make a distinction between failed and
successful commands, such as a terminal. CRPC point of view. CRPC clients should
still parse these error messages.
Here is an end-to-end transaction, sans authentication (see below):
CRPC client issues:
GET /_chatops HTTP/1.1
Accept: application/json
{
"namespace": "deploy",
"help": null,
"version": 3,
"error_response": "The server had an unexpected error. More information is perhaps available in the [error tracker](https://example.com)",
"methods": {
"options": {
"help": "hubot deploy options <app> - List available environments for <app>",
"regex": "options(?: (?<app>\\S+))?",
"params": [
"app"
],
"path": "wcid"
}
}
}
The client will use the suggested namespace
as a prefix, deploy
. Thus, when
the client receives a command matching .deploy options hubot
, the CRPC client
issues:
POST /_chatops/wcid HTTP/1.1
Accept: application/json
Content-type: application/json
Content-length: 77
{"user":"bhuga","method":"options","params":{"app": "hubot"},"room_id":"developer-experience"}
The CRPC server should respond with output like the following:
{"result":"Hubot is unlocked in production, you're free to deploy.\nHubot is unlocked in staging, you're free to deploy.\n"}
The CRPC client should output "Hubot is unlocked in production, you're free to deploy.\nHubot is unlocked in staging, you're free to deploy.\n" to the chat room. The client can optionally display the output intelligently if it contains newlines, links in formats like markdown, etc. It's strongly recommended that a client support markdown links if possible.
Clients authenticate themselves to servers by signing requests with RS256 using a private key. Servers have a public key associated with clients and verify the signature with it.
By convention, a CRPC server should allow authentication with two secrets simultaneously to allow seamless token rolling.
Clients send three additional HTTP headers for authentication: Chatops-Nonce
,
Chatops-timestamp
, and Chatops-Signature
.
Chatops-Nonce
: A random, base64-encoded string unique to every chatops request. Servers can cache seen nonces and refuse to execute them a second time.Chatops-Timestamp
: An ISO 8601 time signature in UTC, such as2017-05-11T19:15:23Z
.Chatops-Signature
: The signature for this request.
The value to be signed is formed by concatenating the value of the full http path,
followed by a newline character, followed by the contents of the nonce
header, followed by a newline character, followed by the value of the timestamp header,
followed by a newline character, followed by the entire HTTP post body, if any. For example,
for a GET
request with these headers:
Chatops-Nonce: abc123
Chatops-Timestamp: 2017-05-11T19:15:23Z
Sent to the following URL:
https://example.com/_chatops
The string to be signed is:
https://example.com/_chatops\nabc123\n2017-05-11T19:15:23Z\n
For a request with the same headers and a POST body of {"method": "foo"}
, the
string to be signed is:
https://example.com/_chatops\nabc123\n2017-05-11T19:15:23Z\n{"method": "foo"}
The signature header starts with the word Signature
, followed by whitespace,
followed by comma-separated key-value pairs separated by an =
. Keys must be
all lowercase.
keyid
: An implementation-specific key identifier that servers can use to determine which private key signed this request.signature
: The base64-encoded RSA-SHA256 signature of the signing string.
An example signature header would be:
Chatops-Signature: Signature keyid=rsakey1,signature=<base64-encoded-signature>
Use the sample server public key to verify the following message with your implementation.
Signature string:
http://test.host/_chatops\n889c9543c22695bc031f723ef2fd28ef1fbed6b0\n2017-06-28T22:51:41Z\n
Signature:
trtOLqLMKzCohxT6Uzeqs+n5Q1msTQUIO4GDl0pyyyTea5MOte6dIQ+k9AlY
HOJ2IHTxGHVhDYJTm2AtgHOEZqrLpqOLqORj64HbwIWtTyuRBUmUmzHWMJKH
a6jy4u9aB8VgSKxE7oDHU6Zo/7kGvqvTBSumF2kMaSjkMXhkUd5WmuQGWpPJ
5hC0W65alCJU1inQQDZDgj1oH/849zZB3WU8Ne61BMM1Qb4IcljDU6UciGyP
OgXRNSALvgKdCSJyhLhHBxYvuypCjUpgiWKm4h3u0GOpem8NoBXLjeEHT4fR
wJYP8hmQWauUgOmjvKt2wufykHZDZNp4fPwkm6qGKg==
(Line breaks added for readability)
CRPC must trust that a user is authenticated by the user
parameter sent with
every command. Individual servers may request a second authentication factor
after receiving a command; this is beyond the scope of CRPC.
CRPC servers are responsible for ensuring that the given user
has the proper
authorization to perform an operation.
Chatops RPC clients are expected to add a few niceties not covered by the wire protocol. This complexity is exported to clients to keep the burden of implementing new automation low.
- Regex anchoring. Clients should anchor regexes received from servers. If a
command is exported as
where can i deploy
, it should not be triggered ontell me where i can deploy
orwhere can i deploy, i'm bored
. - Prefixing. Different execution contexts may prefix commands, such as
.
,hubot
, or another sigil. - Help display systems. These are heavily context dependent. Servers provide text snippets about commands, but accessing and displaying them is up to the client.
These niceties are optional and context-dependent. Different clients may or may not implement them. But if any of these are required in any execution context, they should not be pushed to the server.
The version of the ChatopsRPC protocol in use by a server is given as the
version
field. If no version is returned, 3
is assumed.