Skip to content

Commit e970da9

Browse files
author
Ben Lavender
committed
release docs
1 parent 7885540 commit e970da9

2 files changed

Lines changed: 258 additions & 24 deletions

File tree

README.md

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# Chatops Controller
22

3-
Rails helpers for easy, JSON-RPC based chatops.
3+
Rails helpers for easy and well-tested Chatops RPC. See the [protocol docs](docs/protocol-description.md)
4+
for background information on Chatops RPC.
45

56
A minimal controller example:
67

78
```ruby
89
class ChatopsController < ApplicationController
910
include ::Chatops::Controller
1011

12+
# The default chatops RPC prefix. Clients may replace this.
1113
chatops_namespace :echo
1214

1315
chatop :echo,
14-
/echo (?<text>.*)?/,
15-
"echo <text> - Echo some text back" do
16+
/(?<text>.*)?/,
17+
"<text> - Echo some text back" do
1618
jsonrpc_success "Echoing back to you: #{jsonrpc_params[:text]}"
1719
end
1820
end
@@ -27,30 +29,48 @@ Rails.application.routes.draw do
2729
end
2830
```
2931

32+
It's easy to test:
33+
34+
```
35+
class MyControllerTestCase < ActionController::TestCase
36+
before do
37+
chatops_prefix "echo"
38+
chatops_auth!
39+
end
40+
41+
def test_it_works
42+
chat "echo foo bar baz"
43+
assert_equal "foo bar baz", chatop_response
44+
end
45+
end
46+
```
47+
3048
Before you deploy, add the RPC authentication tokens to your app's environment,
3149
below.
3250

33-
You're all done. Try `.echo foo`, and you should see Hubot respond with `Echoing back to you: foo`.
51+
You're all done. Try `.echo foo`, and you should see Hubot respond with `Echoing
52+
back to you: foo`.
3453

3554
## Usage
3655

3756
#### Namespaces
3857

3958
Every chatops controller has a namespace. All commands associated with this
40-
controller will be displayed with `.help <namespace>`. Commands will also be
41-
dispalyed with subsets of their text.
59+
controller will be displayed with `.<namespace>` in chat. The namespace is a
60+
default chatops RPC prefix and may be overridden by a client.
4261

4362
```
4463
chatops_namespace :foo
4564
```
65+
4666
#### Creating Chatops
4767

4868
Creating a chatop is a DSL:
4969

5070
```ruby
5171
chatop :echo,
52-
/echo (?<text>.*)?/,
53-
"echo <text> - Echo some text back" do
72+
/(?<text>.*)?/,
73+
"<text> - Echo some text back" do
5474
jsonrpc_success "Echoing back to you: #{jsonrpc_params[:text]}"
5575
end
5676
```
@@ -61,12 +81,12 @@ captures](http://ruby-doc.org/core-1.9.3/Regexp.html#method-i-named_captures).
6181
In this example, only one capture group is available, `text`.
6282

6383
The next line is a string, which is a single line of help that will be displayed
64-
in chat for `.help echo`.
84+
in chat for `.echo`.
6585

6686
The DSL takes a block, which is the code that will run when the chat robot sees
6787
this regex. Arguments will be available in the `params` hash. `params[:user]`
68-
and `params[:room_id]` are special, and will be set by hubot. `user` will always
69-
be the login of the user typing the command, and `room_id` will be where
88+
and `params[:room_id]` are special, and will be set by the client. `user` will
89+
always be the login of the user typing the command, and `room_id` will be where
7090
it was typed.
7191

7292
You can return `jsonrpc_success` with a string to return text to chat. If you
@@ -83,24 +103,17 @@ Authentication uses the Chatops v3 public key signing protocol. You'll need
83103
two environment variables to use this protocol:
84104

85105
`CHATOPS_AUTH_PUBLIC_KEY` is the public key of your chatops client in PEM
86-
format. This environment variable will contain newlines.
106+
format. This environment variable will be the contents of a `.pub` file,
107+
newlines and all.
87108

88109
`CHATOPS_AUTH_BASE_URL` is the base URL of your server as the chatops client
89110
sees it. This is specified as an environment variable since rails will trust
90-
client headers about forwarded host. For example, if your chatops client has
91-
added the url `https://example.com/_chatops`, you'd set this to
111+
client headers about a forwarded hostname. For example, if your chatops client
112+
has added the url `https://example.com/_chatops`, you'd set this to
92113
`https://example.com`.
93114

94-
You can also set `CHATOPS_AUTH_ALT_PUBLIC_KEY` to a second public key which
95-
will be accepted. This is helpful when rolling keys.
96-
97-
TODO: link to protocol docs.
98-
99-
## Staging
100-
101-
Use `.rpc set suffix https://myapp.example.com/_chatops in staging`, and all
102-
your chatops will require the suffix `in staging`. This means you can do `.echo
103-
foo` and `.echo foo in staging` to use two different servers to run `.echo foo`.
115+
You can also optionally set `CHATOPS_AUTH_ALT_PUBLIC_KEY` to a second public key
116+
which will be accepted. This is helpful when rolling keys.
104117

105118
## Development
106119

@@ -161,6 +174,9 @@ Becomes:
161174

162175
```ruby
163176
chat "build foobar"
177+
# or
178+
chatops_prefix "ci"
179+
chat "ci build foobar"
164180
```
165181

166182
##### Using public key authentication

docs/protocol-description.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
## Chatops RPC Protocol
2+
3+
CRPC is a client-server protocol; Hubot is a client. Servers expose an endpoint
4+
listing available methods. Each endpoint provides a regex to fire on and a
5+
relative URL path to execute it.
6+
7+
Chatops RPC pushes a lot of complexity to clients. This is a design decision,
8+
intended to keep the burden of creating new chat commands in existing systems
9+
as low as possible.
10+
11+
## Listing Commands
12+
13+
A CRPC service listing is an endpoint that exposes JSON including the following
14+
fields:
15+
16+
* `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\-_]+/`.
17+
* `help`: **Optional:** Overall help for this namespace, if a client chooses to provide help
18+
* `error_response`: **Optional:** A message to present when this endpoint returns an error. This can direct users to next steps when the server fails.
19+
* `methods`: A mapping of named operations to their metadata.
20+
* `version`: The version of ChatOps RPC protocol to use, currently version 3
21+
22+
Each key in the `methods` hash will be a string name. Each name should be a
23+
slug of the form `/[a-Z0-9]\-_]+/`. Clients can use these method names to uniquely
24+
identify methods within a namespace. Each name shall point to an object with the
25+
following fields:
26+
27+
* `regex`: A string regular expression source used to execute the command. This regular expression should use named capture groups of the form `(?<parameter_name>.+)`.
28+
* `path`: A path, relative to the listing URL, to execute the command.
29+
* `params`: A list of available named parameters for this command.
30+
* `help`: **Optional:** User help for a given command.
31+
32+
Each server is assumed to be given a prefix, which the client will handle
33+
prepending to a command's regex source. Clients can use the `namespace` as a
34+
default prefix if they wish, but servers may not demand a particular prefix.
35+
Chatops RPC clients should require whitespace after the prefix, so a command with a
36+
regex like `/ping/` with a prefix of `test` would match on `test ping`.
37+
38+
## Executing Commands
39+
40+
CRPC clients use the listings to create a listing of available commands. When a
41+
chat message matches a command's regex matcher, the CRPC client creates a method
42+
invocation. A method invocation is a JSON object with the following fields:
43+
44+
* `user`: A slug username corresponding to to the command giver's GitHub login.
45+
* `room_id`: A slug room name where the command originated.
46+
* `method`: The method name, without namespace, of the matching regex.
47+
* `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.
48+
49+
The JSON object is posted to the `path` associated with the command from the
50+
listing of commands. CRPC servers should assume that parameters in the `params`
51+
hash are under user control, but trust that the `user` and `room_id` to be
52+
correct.
53+
54+
CRPC servers must produce a response JSON object with the following fields:
55+
56+
* `result`: A string to be displayed in the originating chat room.
57+
58+
CRPC may optionally include the following fields in a response JSON object for
59+
use in situations where richer results can be displayed. Clients will optionally
60+
utilize some or all of the extra information to provide an enhanced response,
61+
but it is important that `result` be sufficient on its own.
62+
63+
* `title`: The title text for the response
64+
* `title_link`: Optional URL to link the title text to
65+
* `color`: Hex color for the message, to indicate status/group e.g. "ddeeaa'
66+
* `buttons`: An array of button objects
67+
* `label`: The text to display on the button
68+
* `image_url`: An image URL to display as the button, will generally take precedence
69+
* `command`: The command to use when the button is clicked
70+
* `image_url`: An image URL to be included with the response
71+
72+
CRPC may also produce error JSON according to the JSON-RPC spec, consisting of
73+
an object containing an `error` object with a `message` string. This is
74+
sometimes helpful for clients that make a distinction between failed and
75+
successful commands, such as a terminal. CRPC point of view. CRPC clients should
76+
still parse these error messages.
77+
78+
## Examples
79+
80+
Here is an end-to-end transaction, sans authentication (see below):
81+
82+
CRPC client issues:
83+
```
84+
GET /_chatops HTTP/1.1
85+
Accept: application/json
86+
87+
{
88+
"namespace": "deploy",
89+
"help": null,
90+
"version": 3,
91+
"error_response": "The server had an unexpected error. More information is perhaps available in the [error tracker](https://example.com)"
92+
"methods": {
93+
"options": {
94+
"help": "hubot deploy options <app> - List available environments for <app>",
95+
"regex": "options(?: (?<app>\\S+))?",
96+
"params": [
97+
"app"
98+
],
99+
"path": "wcid"
100+
}
101+
}
102+
}
103+
```
104+
105+
The client will use the suggested `namespace` as a prefix, `deploy`. Thus, when
106+
the client receives a command matching `.deploy options hubot`, the CRPC client
107+
issues:
108+
109+
```
110+
POST /_chatops/wcid HTTP/1.1
111+
Accept: application/json
112+
Content-type: application/json
113+
Content-length: 77
114+
115+
{"user":"bhuga","method":"wcid","params":{"app": "hubot"},"room_id":"developer-experience"}
116+
```
117+
118+
The CRPC server should respond with output like the following:
119+
120+
```
121+
{"result":"Hubot is unlocked in production, you're free to deploy.\nHubot is unlocked in staging, you're free to deploy.\n"}
122+
```
123+
124+
The CRPC client should output "Hubot is unlocked in production, you're free to
125+
deploy.\nHubot is unlocked in staging, you're free to deploy.\n" to the chat
126+
room. The client can optionally display the output intelligently if it contains
127+
newlines, links in formats like markdown, etc. It's strongly recommended that
128+
a client support markdown links if possible.
129+
130+
## Authentication
131+
132+
#### Authenticating clients
133+
134+
Clients authenticate themselves to servers by signing requests with RS256
135+
using a private key. Servers have a public key associated with clients and
136+
verify the signature with it.
137+
138+
By convention, a CRPC server should allow authentication with two secrets
139+
simultaneously to allow seamless token rolling.
140+
141+
Clients send three additional HTTP headers for authentication: `Chatops-Nonce`,
142+
`Chatops-timestamp`, and `Chatops-Signature`.
143+
144+
* `Chatops-Nonce`: A random, base64-encoded string unique to every chatops
145+
request. Servers can cache seen nonces and refuse to execute them a second time.
146+
* `Chatops-Timestamp`: An ISO 8601 time signature in UTC, such as
147+
`2017-05-11T19:15:23Z`.
148+
* `Chatops-Signature`: The signature for this request.
149+
150+
The value to be signed is formed by concatenating the value of the full http path,
151+
followed by a newline character, followed by the contents of the nonce
152+
header, followed by a newline character, followed by the value of the timestamp header,
153+
followed by a newline character, followed by the entire HTTP post body, if any. For example,
154+
for a `GET` request with these headers:
155+
156+
```
157+
Chatops-Nonce: abc123
158+
Chatops-Timestamp: 2017-05-11T19:15:23Z
159+
```
160+
161+
Sent to the following URL:
162+
163+
`https://example.com/_chatops`
164+
165+
The string to be signed is:
166+
`https://example.com/_chatops\nabc123\n2017-05-11T19:15:23Z\n`
167+
168+
For a request with the same headers and a POST body of `{"method": "foo"}`, the
169+
string to be signed is:
170+
171+
`https://example.com/_chatops\nabc123.2017-05-11T19:15:23Z\n{"method": "foo"}`
172+
173+
The signature header starts with the word `Signature`, followed by whitespace,
174+
followed by comma-separated key-value pairs separated by an `=`. Each
175+
value is closed with double quotes. Keys must be all lowercase.
176+
177+
* `keyid`: An implementation-specific key identifier that servers can use to
178+
determine which private key signed this request.
179+
* `signature`: The base64-encoded RSA-SHA256 signature of the signing string.
180+
181+
An example signature header would be:
182+
183+
`Chatops-Signature: Signature keyid="rsakey1",signature="<base64-encoded-signature>"`
184+
185+
#### Authentication
186+
187+
CRPC must trust that a user is authenticated by the `user` parameter sent with
188+
every command. Individual servers may request a second authentication factor
189+
after receiving a command; this is beyond the scope of CRPC.
190+
191+
#### Authorization
192+
193+
CRPC servers are responsible for ensuring that the given `user` has the proper
194+
authorization to perform an operation.
195+
196+
### Execution
197+
198+
Chatops RPC clients are expected to add a few niceties not covered by the wire
199+
protocol. This complexity is exported to clients to keep the burden of
200+
implementing new automation low.
201+
202+
* Regex anchoring. Clients should anchor regexes received from servers. If a
203+
command is exported as `where can i deploy`, it should not be triggered on
204+
`tell me where i can deploy` or `where can i deploy, i'm bored`.
205+
* Prefixing. Different execution contexts may prefix commands, such as `.`,
206+
`hubot`, or another sigil.
207+
* Help display systems. These are heavily context dependent. Servers provide
208+
text snippets about commands, but accessing and displaying them is up to the
209+
client.
210+
211+
These niceties are optional and context-dependent. Different clients may or may
212+
not implement them. But if any of these are required in any execution context,
213+
they should not be pushed to the server.
214+
215+
### Protocol Changes
216+
217+
The version of the ChatopsRPC protocol in use by a server is given as the
218+
`version` field. If no version is returned, `3` is assumed.

0 commit comments

Comments
 (0)