It took me a decent amount of time figuring out how to sign messages for a ReasonML GitHub app. Here are my findings. tl;dr: code snippets are below, the reason-native labels checker service is open source on GitHub Just before we publish new versions of , ’s internal development , we generate our file using the wonderful . “lerna-changelog will show all pull requests that have been merged since the latest tagged commit in the repository. That is however only true for pull requests with certain labels applied.” GitHub labels are shared between PRs and issues, but only some of them are used for the changelog generation. Yoshi Wix toolchain CHANGELOG.md lerna-changelog In order to reduce confusion, we chose to have `PR:` prefix to all labels that affects the changelog, so maintainers would know to choose at least one of them for each PR. Sometimes, we forget to add a label, and that causes the PR to not be in the changelog. That may be totally fine for some projects, but we want to be as transparent as we can, therefore “hiding” a PR hurts our core values as a team. I decided to write a simple app that will add a status check that passes only if the PR has a valid changelog label, triggered by GitHub’s webhooks. I wanted to write it in native Reason, so I’ll learn how to use the native toolchain better. So, instead of compiling to JS and running it with Node.js, like most tutorials around Reason show, I decided to use native OCaml and produce a static binary. This creates a lightweight executable (11mb!) that can be deployed fast, compared to standard Node apps, that need the entire runtime. , for those who want to join in or just take a look. OCaml very The source code is on GitHub At first, all I did was making a simple server that works with GitHub webhooks that accept a user token as a query parameter that I’ll later pass to the query param in the GitHub API. While this works well, it is “less secure”, because all the repository admins will have access to some user’s token. Generating a user just to make API calls is also less secure than just using GitHub apps. So I decided to go with GitHub apps. access_token The only difference between using webhooks to GitHub apps is an authorization process. Apps are using signed JWT, as mentioned in to request a short-living token for a specific installation (or, a repo). Then, the rest is the same — we get a token, just like we got from the query param. RS256 the docs In order to make JWTs I tried to use the library, but unfortunately, it doesn’t support RS256 out of the box, although there is a PR that adds the support, so I decided to do that from “scratch”: searching for a library to sign my request and build the JWT myself. ocaml-jwt Since I’m pretty new to OCaml, I’m not aware of libraries so I’m just googling around and looking for packages. After a time of research, I decided to go with (along with ), when I saw a wild github repo called — A function that takes a private key, a string and returns a signed string. Here’s the function in a Reason syntax: long [nocrypto](https://github.com/mirleft/ocaml-nocrypto) [x509](https://github.com/mirleft/ocaml-x509) ocaml-letsencrpt implementing exactly what I needed I’m using 3 libraries here: , , and . So make sure you install them before using it in your project: nocrypto x509 cstruct opam install nocrypto x509 cstruct The function above takes a private key and a string, then signs the string. So we need to get the private key, and specifically a private key. Here’s a fast way of reading a PEM file into a RSA private key: Nocrypto Nocrypto Now we can simply take our private key and sign requests. . It’s pretty straight-forward, yet hacky — I am using hard-coded stringified JSON, and I’m not even sorry — it feels secure because of the type checking. It formats some s, so no escape necessary for JSON. feels right. The rest of the JWT-generation code lives here int , documentation and entry barrier aren’t the strongest sides of OCaml/Reason land, so I hope this will help others, or maybe even the future me. Don’t hesitate to or just respond here. I’d love to chat! As I already mentioned before ping me on twitter Cheers!