matrix-hookshot

Previously matrix-github

#hookshot:half-shot.uk Docker Image Version (latest by date)

A bridge between Matrix and multiple project management services, such as GitHub, GitLab and JIRA.

Featureset

This bridge supports:

  • Figma
    • File comments
  • GitHub
    • Webhooks (new issues, pull requests, releases etc)
    • Commands (create issues, assign issues, start workflows etc)
  • GitLab
    • Webhooks (new issues, merge requests etc)
    • Commands
  • Jira
    • Webhooks (new issues, issue changes)
    • Commands (create new issues)
  • Generic webhooks
    • Webhooks (via GET, PUT or POST with optional transformation functions)

Setup

See the setup guide

Documentation

Documentation can be found on GitHub Pages.

You can build the documentaion yourself by:

# cargo install mdbook
mdbook build
sensible-browser book/index.html

Contact

We have a bridge support room you can drop into at #hookshot:half-shot.uk, or you can reach me at @Half-Shot:half-shot.uk

Getting setup

This page explains how to set up Hookshot for use with a Matrix homeserver.

Requirements

Hookshot is fairly light on resources, and can run in as low as 100MB or so of memory. Hookshot memory requirements may increase depending on the traffic and the number of rooms bridged.

Local installation

This bridge requires at least Node 12 (though 16 is preferred), and Rust installed.

To install Node.JS, nvm is a good option.

To install Rust, rustup is the preferred solution to stay up to date.

To clone and install, run:

git clone git@github.com:Half-Shot/matrix-hookshot.git
cd matrix-hookshot
yarn # or npm i

Starting the bridge (after configuring it), is a matter of running yarn start.

Installation via Docker

To get started quickly, you can use the Docker image halfshot/matrix-hookshot

docker run \
    --name matrix-hookshot \
    -d \
    -p 9993:9993 \ # Homeserver port
    -p 9000:9000 \ # Webhook port
    -p 9002:9002 \ # Metrics port
    -v /etc/matrix-hookshot:/data \
    halfshot/matrix-hookshot:latest

Where /etc/matrix-hookshot would contain the configuration files config.yml and registration.yml, along with any other files needed.

Configuration

Copy the config.sample.yml to a new file config.yml. The sample config is also hosted here for your convienence.

You should read and fill this in as the bridge will not start without a complete config.

You may validate your config without starting the service by running yarn validate-config.

Copy registration.sample.yml into registration.yml and fill in:

  • At a minimum, you will need to replace the as_token and hs_token and change the domain part of the namespaces.

You will need to link the registration file to the homeserver. Consult your homeserver documentation on how to add appservices. Synapse documents the process here.

Listeners configuration

You will need to configure some listeners to make the bridge functional.

listeners:
  # (Optional) HTTP Listener configuration.
  # Bind resource endpoints to ports and addresses.
  # 'resources' may be any of webhooks, widgets, metrics, provisioning
  #
  - port: 9000
    bindAddress: 0.0.0.0
    resources:
      - webhooks
  - port: 9001
    bindAddress: 127.0.0.1
    resources:
      - metrics
      - provisioning

At a minimum, you should bind the webhooks resource to a port and address. You can have multiple resources on the same port, or one on each. Each listener MUST listen on a unique port.

You will also need to make this port accessible to the internet so services like GitHub can reach the bridge. It is recommended to factor hookshot into your load balancer configuration, but currrently this process is left as an excercise to the user.

In terms of API endpoints:

  • The webhooks resource handles resources under /, so it should be on it's own listener. Note that OAuth requests also go through this listener.
  • The metrics resource handles resources under /metrics.
  • The provisioning resource handles resources under /v1/....

Appservice listener

Please note that the appservice HTTP listener is configured seperately from the rest of the bridge due to lack of support in the upstream library. See this issue for details.
bridge:
  # Basic homeserver configuration
  #
  domain: example.com
  url: http://localhost:8008
  mediaUrl: http://example.com
  port: 9993
  bindAddress: 127.0.0.1

The port and bindAddress must not conflict with the other listeners in the bridge config. This listeners should not be reachable over the internet to users, as it's intended to be used by the homeserver exclusively. This service listens on /_matrix/app/.

Services configuration

You will need to configure some services. Each service has it's own documentation file inside the the setup subdirectory.

Sample Configuration

Below is a sample bridge configuration file. The configuration file can be tweaked to change the behaviour of your bridge. A bridge of the server is required to apply any changes made to this file.

# This is an example configuration file

bridge:
  # Basic homeserver configuration
  #
  domain: example.com
  url: http://localhost:8008
  mediaUrl: http://example.com
  port: 9993
  bindAddress: 127.0.0.1
github:
  # (Optional) Configure this to enable GitHub support
  #
  auth:
    # Authentication for the GitHub App.
    #
    id: 123
    privateKeyFile: github-key.pem
  webhook:
    # Webhook settings for the GitHub app.
    #
    secret: secrettoken
  oauth:
    # (Optional) Settings for allowing users to sign in via OAuth.
    #
    client_id: foo
    client_secret: bar
    redirect_uri: https://example.com/bridge_oauth/
  defaultOptions:
    # (Optional) Default options for GitHub connections.
    #
    showIssueRoomLink: false
gitlab:
  # (Optional) Configure this to enable GitLab support
  #
  instances:
    gitlab.com:
      url: https://gitlab.com
  webhook:
    secret: secrettoken
jira:
  # (Optional) Configure this to enable Jira support
  #
  webhook:
    secret: secrettoken
  oauth:
    client_id: foo
    client_secret: bar
    redirect_uri: https://example.com/bridge_oauth/
generic:
  # (Optional) Support for generic webhook events. `allowJsTransformationFunctions` will allow users to write short transformation snippets in code, and thus is unsafe in untrusted environments
  #
  enabled: false
  urlPrefix: https://example.com/mywebhookspath/
  allowJsTransformationFunctions: false
  userIdPrefix: webhooks_
figma:
  # (Optional) Configure this to enable Figma support
  #
  publicUrl: https://example.com/hookshot/
  instances:
    your-instance:
      teamId: your-team-id
      accessToken: your-personal-access-token
      passcode: your-webhook-passcode
provisioning:
  # (Optional) Provisioning API for integration managers
  #
  secret: "!secretToken"
passFile:
  # A passkey used to encrypt tokens stored inside the bridge.
  # Run openssl genpkey -out passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096 to generate
  #
  passkey.pem
bot:
  # (Optional) Define profile information for the bot user
  #
  displayname: GitHub Bot
  avatar: mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d
metrics:
  # (Optional) Prometheus metrics support
  #
  enabled: true
queue:
  # (Optional) Message queue / cache configuration options for large scale deployments
  #
  monolithic: true
  port: 6379
  host: localhost
logging:
  # (Optional) Logging settings. You can have a severity debug,info,warn,error
  #
  level: info
listeners:
  # (Optional) HTTP Listener configuration.
  # Bind resource endpoints to ports and addresses.
  # 'port' must be specified. Each listener must listen on a unique port.
  # 'bindAddress' will default to '127.0.0.1' if not specified, which may not be suited to Docker environments.
  # 'resources' may be any of webhooks, widgets, metrics, provisioning
  #
  - port: 9000
    bindAddress: 0.0.0.0
    resources:
      - webhooks
  - port: 9001
    bindAddress: 127.0.0.1
    resources:
      - metrics
      - provisioning

Figma

Setting up

To bridge Figma webhooks with Hookshot, you will need:

  • A personal access token with admin access to the team you intend to bridge.
  • A figma account that is on the professional tier, as the free tier does provide webhook access.
  • Your team ID. You can get this by going to the team page on Figma, and looking for the ID in the url (e.g. 12345 in https://www.figma.com/files/team/12345/...)

Configuration

You can now set some configuration in the bridge config.yml

figma:
  publicUrl: https://example.com/hookshot/
  instances:
    your-instance:
      teamId: your-team-id
      accessToken: your-personal-access-token
      passcode: your-webhook-passcode

your-instance should be a friendly name for your instance E.g. matrix-dot-org.

The publicUrl value must be the public path to /figma/webhook on the webhooks listener. E.g. if your load balancer points https://example.com/hookshot to the bridge's webhooks listener, you should use the path https://example.com/hookshot/figma/webhook.

The accessToken should be the personal access token for your account.

The passcode should be a randomly generated code which is used to authenticate requests from Figma.

The bridge will automatically setup a webhook on Figma for you upon startup, and will automatically reconfigure that webhook if the publicUrl or passcode changes.

Next steps

If you have followed these steps correctly, Figma should now be configured with hookshot 🥳.

To bridge a figma file into your room, you should:

  • Invite the bot user to the room.
  • Make sure the bot able to send state events (usually the Moderator power level in clients)
  • Say !hookshot figma file fileUrl where fileUrl is the URL to the figma file e.g https://www.figma.com/files/project/12345/...
  • Figma comments will now be bridged into the room.

Setting up GitHub

GitHub App

This bridge requires a GitHub App. You will need to create one.

Most of the configuration can be left up to you, but the important points are:

  • The callback URL should be set if you plan to use OAuth.
  • The webhook URL should point to the public address of your hookshot instance, at the / path. A secret must be given.
  • A client secret should be generated and stored.

You will need to enable the following permissions:

  • Repository
    • Actions (read)
    • Contents (read)
    • Discussions (read & write)
    • Issues (read & write)
    • Metadata
    • Projects (read & write)
    • Pull requests (read & write)
  • Organisation
    • Discussions (read)

Hookshot handles the following webhook event types:

  • Commit comment
  • Create
  • Delete
  • Discussion
  • Discussion comment
  • Issue comment
  • Issues
  • Project
  • Project card
  • Project column
  • Pull request
  • Pull request review
  • Pull request review comment
  • Push
  • Release
  • Repository
  • Workflow run

You can disable any of these to disable the events being handled in Hookshot.

Once you have setup your app, you can move onto configuring the bridge:

Configuration

The GitHub service requires a few connection options.

github:
  auth:
    id: 123
    privateKeyFile: github-key.pem
  webhook:
    secret: secrettoken
  oauth:
    client_id: foo
    client_secret: bar
    redirect_uri: https://example.com/bridge_oauth/
  defaultOptions:
    showIssueRoomLink: false

In the auth section, you will need to supply the App ID given in your GitHub App page. You should also supply the page to a generated private key file.

The webhook section takes a secret, which is Webhook secret on the GitHub App page.

The oauth section should include both the Client ID and Client Secret on the GitHub App page. The redirect_uri value must be the public path to /oauth on the webhooks path. E.g. if your load balancer points https://example.com/hookshot to the bridge webhooks listener, you should use the path https://example.com/hookshot/oauth. This value MUST exactly match the Callback URL on the GitHub App page.

defaultOptions allows you to set some defaults for room connections. Options listed on this page are supported.

Next steps

If you have followed these steps correctly, GitHub should now be configured with hookshot 🥳.

You can now follow the guide on authenticating with GitHub, and then bridging a room

GitLab

Configuration

GitLab configuration is fairly straight-forward:

  # (Optional) Configure this to enable GitLab support
  #
  instances:
    gitlab:
      url: https://gitlab.com
  webhook:
    secret: secrettoken

You neeed to list all the instances you plan to connect to in the config.yml. This is used so that users can give a short name like gitlab or matrix.org when they want to specify an instance.

The webhooks secret should be generated, for use in your repositories.

Adding a repository

Adding a repository is a case of navigating to the settings page, and then adding a new webhook. You will want to give the URL of the public address for the hookshot webhooks port on the / path.

You should add the events you wish to trigger on. Hookshot currently supports:

  • Push events
  • Tag events
  • Issues events
  • Merge request events
  • Releases events

You will need to do this each time you want to a repository to hookshot.

To then bridge a room to GitLab, you will need to add a uk.half-shot.matrix-hookshot.gitlab.repository state event to a room containing a content of:

{
    "instance": "gitlab", // your instance name
    "path": "yourusername/repo" // the full path to the repo
}

Once this is done, you are bridged 🥳.

JIRA

Adding a webhook to a JIRA Organisation

This should be done for all JIRA organisations you wish to bridge. The steps may differ for SaaS and on-prem, but you need to go to the webhooks configuration page under Settings > System.

Next, add a webhook that points to / on the public webhooks address for hookshot. You should also include a secret value by appending ?secret=your-webhook-secret. The secret value can be anything, but should be reasonably secure and should also be stored in the config.yml file.

Ensure that you enable all the events that you wish to be bridge.

JIRA OAuth

The JIRA service currently only supports atlassian.com (JIRA SaaS) when handling user authentication. Support for on-prem deployments is hoping to land soon.

You will need a Atlassain account with the ability to use the developer tools in order to create the app.

You'll first need to head to https://developer.atlassian.com/console/myapps/create-3lo-app/ to create a "OAuth 2.0 (3LO)" integration.

Once named and created, you will need to: - Enable the User REST, Jira Platform REST and User Identity APIs under Permissions. - Use rotating tokens under Authorisation. - Set a callback url. This will be the public URL to hookshot with a path of /jira/oauth. - Copy the client ID and Secret from Settings

Configuration

You can now set some configuration in the bridge config.yml

jira:
  webhook:
    secret: some-secret
  oauth:
    client_id: your-client-id
    client_secret: your-client-secret
    redirect_uri: https://example.com/hookshot/jira/oauth

You can leave the oauth section blank if you are not planning to use those capabilities.

The redirect_uri value must be the public path to /jira/oauth on the webhooks path. E.g. if your load balancer points https://example.com/hookshot to the bridge webhooks listener, you should use the path https://example.com/hookshot/jira/oauth. This value MUST exactly match the Callback URL on the JIRA integration page page.

Next steps

If you have followed these steps correctly, JIRA should now be configured with hookshot 🥳.

You can now follow the guide on authenticating with JIRA.

Webhooks

Hookshot supports generic webhook support so that services can send messages into Matrix rooms without being aware of the Matrix protocol. This works by having services hit a unique URL that then transforms a HTTP payload into a Matrix message.

Configuration

You will need to add the following configuration to the config file.

generic:
  enabled: true
  urlPrefix: https://example.com/mywebhookspath/
  allowJsTransformationFunctions: false

The bridge listens for incoming webhooks requests on the host and port provided in the listeners config.

urlPrefix describes the public facing URL of your webhook handler. For instance, if your load balancer redirected webhook requests from https://example.com/mywebhookspath to the bridge an example webhook URL would look like: https://example.com/mywebhookspath/abcdef.

Adding a webhook

To add a webhook to your room:

  • Invite the bot user to the room.
  • Make sure the bot able to send state events (usually the Moderator power level in clients)
  • Say !hookshot webhook example where example is a name for your hook.
  • The bot will respond with the webhook URL to be sent to services.

Webhook Handling

Hookshot handles HTTP requests with a method of GET, POST or PUT.

If the request is a GET request, the query parameters are assumed to be the body. Otherwise, the body of the request should be a JSON payload.

If the body contains a text key, then that key will be used as a message body in Matrix (aka body). This text will be automatically converted from Markdown to HTML (unless a html key is provided.).

If the body contains a html key, then that key will be used as the HTML message body in Matrix (aka formatted_body). A text key fallback MUST still be provided.

If the body also contains a username key, then the message will be prepended by the given username. This will be prepended to both text and html.

If the body does NOT contain a text field, the full JSON payload will be sent to the room. This can be adapted into a message by creating a JavaScript transformation function.

JavaScript Transformations

Although every effort has been made to securely sandbox scripts, running untrusted code from users is always risky. Ensure safe permissions in your room to prevent users from tampering with the script.

This bridge supports creating small JavaScript snippets to translate an incoming webhook payload into a message for the room, giving you a very powerful ability to generate messages based on whatever input is coming in.

The input is parsed and exectuted within a seperate JavaScript Virtual Machine context, and is limited to an execution time of 2 seconds. With that said, the feature is disabled by default and allowJsTransformationFunctions must be enabled in the config.

The code snippets can be edited by editing the Matrix state event corresponding to this connection (with a state type of uk.half-shot.matrix-hookshot.generic.hook). Because this is a fairly advanced feature, this documentation won't go into how to edit state events from your client. Please seek out documentation from your client on how to achieve this.

The script string should be set within the state event under the transformationFunction key.

Script API

The scripts have a very minimal API. The execution environment will contain a data field, which will be the body of the incoming request (JSON will be parsed into an Object). Scripts are executed syncronously and a variable result is expected to be set in the execution, which will be used as the text value for the script. result will be automatically transformed by a Markdown parser.

If the script contains errors or is otherwise unable to work, the bridge will send an error to the room.

Example script

Where data = {"counter": 5, "maxValue": 4}

if (data.counter > data.maxValue) {
    result = `**Oh no!** The counter has gone over by ${data.counter - data.maxValue}`
} else {
    result = `*Everything is fine*, the counter is under by ${data.maxValue - data.counter}`
}

Usage

This section covers how to use the bridge, once it's set up. We break these down into categories:

  • Authentication: How to connect your user account to a remote service to use rich commands.
  • Room Connections: How to start connecting rooms to services.

If you are looking for help on what bot commands you can run in a room, you can refer to the help information by saying*:

  • !gh help in rooms connected to GitHub.
  • !gl help in rooms connected to GitLab.
  • !jira help in rooms connected to JIRA.

*the prefix may vary if the commandPrefix configuration in the room is set.

Dynamic Rooms

Anyone who has access to your homeserver can query these aliases (even over federation), and aliases do not support checking if a user is authorised to view the content before creation. If you are bridging non-public content, it is advisable to disable this feature.

Some bridges support dynamically creating rooms that point to resources based on an alias given by a user.

Presently, the following are supported:

  • #github_$owner:example.com - For a Matrix space containing a user's discussions and repositories
  • #github_$owner_$repo:example.com - For GitHub repositories
  • #github_$owner_$repo_$issuenumber:example.com - For GitHub issues
  • #github_disc_$owner_$repo:example.com - For GitHub discussions for a repository

Where $word is replaced by the appropriate value.

(Some of these may not be supported, depending on bridge configuration and registration file changes)

Disabling support

This feature can be disabled simply by removing alias fields from the registration file.

Authenticating

To authenticate with services, you must first have a DM room with the bridge set up. In this guide, we are going to assume the bot is called @hookshot:example.com but this will vary for your setup. For all the instructions below, commands should only be executed in the DM room.

GitHub

You can authenticate via OAuth or a Personal Access Token (PAT) when using GitHub. Authentication is required when trying to bridge GitHub resources into rooms.

Please note that you will need a Personal Access Token in order to bridge your personal GitHub notifications. This is a limitation of GitHub's API.

To authenticate with a personal access token:

  1. Open https://github.com/settings/tokens (Github > Settings > Developer Settings / Personal access tokens)

  2. Click Generate new token

  3. Give it a good name, and a sensible expiration date. For scopes you will need:

    • Repo (to access repo information)
      • public_repo
      • repo:status
    • Workflow (if you want to be able to launch workflows / GitHub actions from Matrix)
    • Notifications (if you want to bridge in your notifications to Matrix)
    • User
      • read:user
    • write:discussion (for GitHub discussion support)
      • read:discussion
  4. Send the generated token to the bridge by saying github setpersonaltoken %your-token%. You can redact the message afterwards if you like.

  5. The bridge will have connected you. You can check the status at any time by saying github hastoken

GitLab

You can authenticate with GitLab by supplying a Personal Access Token. OAuth-style authentication isn't supported yet.

  • You will need to have configured a GitLab instance in your config.yml for the instance you want to login to.
  • Open https://%instance%/-/profile/personal_access_tokens (GitLab > User Settings > Access Tokens), where instance is your GitLab instance address.
    • For the public GitLab server, this would be "gitlab.com"
  • Give it a good name, and a sensible expiration date. For scopes you will need to tick api.
  • Send the generated token to the bridge by saying gitlab personaltoken %instance% %your-token%. You can redact the message afterwards if you like.
  • The bridge will have connected you. You can check the status at any time by saying gitlab hastoken %instance%

JIRA

You can login to JIRA via OAuth. This means you will need to have configured OAuth support in your config.yml, and have the endpoints required accessible from the internet. Authentication is required when trying to bridge JIRA resources into rooms.

  • Say jira login to get the URL to authenticate via.
  • Click the URL sent by the bot.
  • Follow the steps, ensuring you authenticate with the right user.
  • If all goes well, you will now be connected. You can check the status and authorisatied instances by saying jira whoami

Room Configuration

Hookshot works off the principle of Connections.

A room can have many connections to different services. The connections are defined in the room state of a room. A connection defines the service it connects to, the type of integration (e.g. GitHub repo, Jira Project) and any additional configuration.

image/svg+xml Matrix Room Connection Connection !foo:example.com
Figure 1. An example of a room connected to GitHub and JIRA

Hookshot supports several connection types, which are defined under the Room Configuration heading.

The availablilty of connection types depends on the configuration provided to hookshot.

The !hookshot command

Rooms can be bridged by inviting the hookshot bot into a room, and then running the !hookshot command. Running !hookshot help will give you some details, but you should see the documentation provided for information on each connection type.

GitHub Repository

This connection type connects a GitHub repository (e.g. https://github.com/Half-Shot/matrix-hookshot) to a room.

You can run commands to create and manipulate issues, and receive notifications when something changes such as a new release.

Setting up

To set up a connection to a GitHub Repository in a new room:

(N.B you must have permission to bridge GitHub repositories before you can use this command, see auth)

  1. Invite the bridge bot (e..g @hookshot:example.com)
  2. Give the bridge bot moderator permissions or higher (power level 50).
  3. Send the command !hookshot github repo https://github.com/my/project
  4. If you have permission to bridge this repo, the bridge will respond with a confirmation message.
  5. Note: The bridge will need to either:
    • Have a GitHub installation registered with the organisation
    • The requesting user must be authenticated with the bridge via OAuth and the repository must be part of their GitHub account.

Configuration

This connection supports a few options which can be defined in the room state:

OptionDescriptionAllowed valuesDefault
ignoreHooksChoose to exclude notifications for some event typesArray of: Supported event typesempty
commandPrefixChoose the prefix to use when sending commands to the botA string, ideally starts with "!"!gh
showIssueRoomLinkWhen new issues are created, provide a Matrix alias link to the issue roomtrue/falsefalse
prDiffShow a diff in the room when a PR is created, subject to limits{enabled: boolean, maxLines: number}{enabled: false}
includingLabelsOnly notify on issues matching these label namesArray of: String matching a label nameempty
excludingLabelsNever notify on issues matching these label namesArray of: String matching a label nameempty

Supported event types

This connection supports sending messages when the following actions happen on the repository.

  • issue
    • issue.created
    • issue.changed
    • issue.edited
  • pull_request
    • pull_request.closed
    • pull_request.merged
    • pull_request.opened
    • pull_request.ready_for_review
    • pull_request.reviewed
  • release
    • release.created

Prometheus Metrics

You can configure metrics support by adding the following to your config:

  enabled: true
  bindAddress: 127.0.0.1
  port: 9002

Below is the generated list of Prometheus metrics for Hookshot.

hookshot

MetricHelpLabels
hookshot_webhooks_http_requestNumber of requests made to the hookshot webhooks handlerpath, method
hookshot_provisioning_http_requestNumber of requests made to the hookshot webhooks handlerpath, method
hookshot_queue_event_pushesNumber of events pushed through the queueevent
hookshot_notifications_pushNumber of notifications pushedservice
hookshot_notifications_service_upIs the notification service up or downservice
hookshot_notifications_watchersNumber of notifications watchers runningservice

matrix

MetricHelpLabels
matrix_api_callsThe number of Matrix client API calls mademethod
matrix_api_calls_failedThe number of Matrix client API calls which failedmethod
matrix_appservice_eventsThe number of events sent over the AS API

process

MetricHelpLabels
process_cpu_user_seconds_totalTotal user CPU time spent in seconds.
process_cpu_system_seconds_totalTotal system CPU time spent in seconds.
process_cpu_seconds_totalTotal user and system CPU time spent in seconds.
process_start_time_secondsStart time of the process since unix epoch in seconds.
process_resident_memory_bytesResident memory size in bytes.
process_virtual_memory_bytesVirtual memory size in bytes.
process_heap_bytesProcess heap size in bytes.
process_open_fdsNumber of open file descriptors.
process_max_fdsMaximum number of open file descriptors.

nodejs

MetricHelpLabels
nodejs_eventloop_lag_secondsLag of event loop in seconds.
nodejs_eventloop_lag_min_secondsThe minimum recorded event loop delay.
nodejs_eventloop_lag_max_secondsThe maximum recorded event loop delay.
nodejs_eventloop_lag_mean_secondsThe mean of the recorded event loop delays.
nodejs_eventloop_lag_stddev_secondsThe standard deviation of the recorded event loop delays.
nodejs_eventloop_lag_p50_secondsThe 50th percentile of the recorded event loop delays.
nodejs_eventloop_lag_p90_secondsThe 90th percentile of the recorded event loop delays.
nodejs_eventloop_lag_p99_secondsThe 99th percentile of the recorded event loop delays.
nodejs_active_handlesNumber of active libuv handles grouped by handle type. Every handle type is C++ class name.type
nodejs_active_handles_totalTotal number of active handles.
nodejs_active_requestsNumber of active libuv requests grouped by request type. Every request type is C++ class name.type
nodejs_active_requests_totalTotal number of active requests.
nodejs_heap_size_total_bytesProcess heap size from Node.js in bytes.
nodejs_heap_size_used_bytesProcess heap size used from Node.js in bytes.
nodejs_external_memory_bytesNode.js external memory size in bytes.
nodejs_heap_space_size_total_bytesProcess heap space size total from Node.js in bytes.space
nodejs_heap_space_size_used_bytesProcess heap space size used from Node.js in bytes.space
nodejs_heap_space_size_available_bytesProcess heap space size available from Node.js in bytes.space
nodejs_version_infoNode.js version info.version, major, minor, patch
nodejs_gc_duration_secondsGarbage collection duration by kind, one of major, minor, incremental or weakcb.kind

Provisioning

This section is not complete yet for end users. For developers, you can read the documentation for the API below:

Provisioning API for matrix-hookshot

Overview

This document describes how to integrate with matrix-hookshot's provisoning API.

Requests made to the bridge must be against the API listener defined in the config under provisioning, not the appservice or webhook listeners.

Requests should always be authenticated with the secret given in the config, inside the Authorization header. Requests being made on behalf of users (most provisioning APIs) should include the userId as a query parameter.

GET /v1/health?userId=%40Half-Shot%3Ahalf-shot.uk
Authorization: Bearer secret

APIs are versioned independently so two endpoints on the latest version may not always have the same version.

APIs

GET /v1/health

Request the status of the provisoning API.

Response

HTTP 200
{}

Any other response should be considered a failed request (e.g. 404, 502 etc).

GET /v1/connectiontypes

Request the connection types enabled for this bridge.

Response

{
    "JiraProject": {
        "type": "JiraProject", // The name of the connection
        "eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type for the connection
        "service": "jira", // or github, webhook. A human-readable service name to make things look pretty
        "botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
    }
}

GET /v1/{roomId}/connections

Request the connections for a given room. The {roomId} parameter is the target Matrix room.

Response

[{
    "type": "JiraProject", // The name of the connection
    "eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
    "id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
    "service": "jira", // or github, webhook. A human-readable service name to make things look pretty
    "botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
    "config": {
        // ... connection specific details, can be configured.
    }
}]

GET /v1/{roomId}/connections/{id}

Request details of a single connection. The {roomId} parameter is the target Matrix room.

Response

{
    "type": "JiraProject", // The name of the connection
    "eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
    "id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
    "service": "jira", // or github, webhook. A human-readable service name to make things look pretty
    "botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
    "config": {
        // ... connection specific details, can be configured.
    }
}

PUT /v1/{roomId}/connections/{type}

Create a new connection of a given type. The type refers to the eventType (IConnection.CanonicalEventType). The {roomId} parameter is the target Matrix room.

The body of the request is the configuration for the connection, which will be the "ConnectionState" interface for each connection.

Request body

{
    // ... connection specific details, can be configured.
}

Response

{
    "type": "JiraProject", // The name of the connection
    "eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
    "id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
    "service": "jira", // or github, webhook. A human-readable service name to make things look pretty
    "botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
    "config": {
        // ... connection specific details, can be configured.
    }
}

PATCH /v1/{roomId}/connections/{id}

Update a connection's configuration. The id refers to the id returned in the GET response.

The body of the request is the configuration for the connection, which will be the "ConnectionState" interface for each connection.

Request body

{
    // ... connection specific details, can be configured.
}

Response

{

    "type": "JiraProject", // The name of the connection
    "eventType": "uk.half-shot.matrix-hookshot.jira.project", // Corresponds to the state type in the connection
    "id": "opaque-unique-id", // An opaque ID used to refer to this connection. Should **NOT** be assumed to be stable.
    "service": "jira", // or github, webhook. A human-readable service name to make things look pretty
    "botUserId": "@hookshot:yourdomain.com", // The bot mxid for the service. Currently this is the sender_localpart, but may change in the future.
    "config": {
        // ... connection specific details, can be configured.
    }
}

DELETE /v1/{roomId}/connections/{id}

Delete a connection. The id refers to the id returned in the GET response.

Response

{
    "ok": true
}

Service specific APIs

Some services have specific APIs for additional functionality, like OAuth.

GitHub

GET /v1/github/oauth?userId={userId}

Request an OAuth url for the given user. Once the user has completed the steps in the OAuth process, the bridge will be granted access.

Response

[{
    "user_url": "https://github.com/login/oauth/authorize?...",
    "org_url": "https://github.com/apps/matrix-bridge/installations/new",
}]

GET /v1/github/account?userId={userId}

Request the status of the users account. This will return a loggedIn value to determine if the bridge has a GitHub identity stored for the user, and any organisations they have access to.

Response

{
    "loggedIn": true,
    "organisations":[{
        "name": "half-shot",
        "avatarUrl": "https://avatars.githubusercontent.com/u/8418310?v=4"
    }]
}

GET /v1/github/orgs/{orgName}/repositories?userId={userId}&page={page}&perPage={perPage}

Request a list of all repositories a user is a member of in the given org. The owner and name value of a repository can be given to create a new GitHub connection.

This request is paginated, and page sets the page (defaults to 1) while perPage (defaults to 10) sets the number of entries per page.

This request can be retried until the number of entries is less than the value of perPage.

Response

{
    "loggedIn": true,
    "repositories":[{
        "name": "matrix-hookshot",
        "owner": "half-shot",
        "fullName": "half-shot/matrix-hookshot",
        "avatarUrl": "https://avatars.githubusercontent.com/u/8418310?v=4",
        "description": "A bridge between Matrix and multiple project management services, such as GitHub, GitLab and JIRA. "
    }]
}

GET /v1/github/repositories?userId={userId}&page={page}&perPage={perPage}

Request a list of all repositories a user is a member of (including those not belonging to an org). The owner and name value of a repository can be given to create a new GitHub connection.

If the user has only allowed a subset of repositories to be bridged, changeSelectionUrl will be defined and can be used to expand the search query.

This request is paginated, and page sets the page (defaults to 1) while perPage (defaults to 10) sets the number of entries per page.

This request can be retried until the number of entries is less than the value of perPage.

Response

{
    "loggedIn": true,
    "changeSelectionUrl": "https://github.com/settings/installations/12345",
    "repositories":[{
        "name": "matrix-hookshot",
        "owner": "half-shot",
        "fullName": "half-shot/matrix-hookshot",
        "avatarUrl": "https://avatars.githubusercontent.com/u/8418310?v=4",
        "description": "A bridge between Matrix and multiple project management services, such as GitHub, GitLab and JIRA. "
    }]
}

JIRA

GET /v1/jira/oauth?userId={userId}

Request an OAuth url for the given user. Once the user has completed the steps in the OAuth process, the bridge will be granted access.

Response

{
    "url": "https://auth.atlassian.com/authorize?..."
}

GET /v1/jira/account?userId={userId}

Request the status of the users account. This will return a loggedIn value to determine if the bridge has a JIRA identity stored for the user, and any instances they have access to. Note that if a user does not have access to an instance, they can authenticate again to gain access to it (if they are able to consent).

Response

{
    "loggedIn": true,
    "instances":[{
        "name": "acme",
        "url": "https://acme.atlassian.net"
    }]
}

GET /v1/jira/instances/{instanceName}/projects?userId={userId}

Request a list of all projects a user can see in a given instance. The url value of a project can be given to create a new JIRA connection.

Response

[{
    "key": "PLAY",
    "name": "Jira Playground",
    "url": "https://acme.atlassian.net/projects/PLAY"
}]

Workers

Hookshot supports running in a worker configuration, using Redis as the middleman process to handle traffic between processes.

This feature is experimental and should only be used when you are reaching natural limits in the monolith process.

Running in multi-process mode

You must first have a working redis instance somewhere which can talk between processes. For example, in Docker you can run:

docker run --name github-bridge-redis -p 6379:6379 -d redis.

The processes should all share the same config, which should contain the correct config enable redis:

queue:
  monolithic: true
  port: 6379
  host: github-bridge-redis

Once that is done, you can simply start the processes by name using yarn:

yarn start:webhooks
yarn start:matrixsender 
yarn start:app

Be aware that you will need to start all worker types when running in worker mode, as the service does not allow a hybrid worker approach.