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