Once upon a time I was need to configure a CI for a private github repo. Public CI providers such as Travis, CircleCI and other don’t provide free plans for private repos. Besides tests I wanted to run are not publicly available. So I decided to provision a Jenkins server inside corporate VPN. How to trigger a pipeline on PR? Github provides a webhooks mechanism. Generally speaking it’s a just HTTP request with some payload. And here we have a problem. How to pass a github webhook to the Jenkins which is not exposed to the Internet?

Possible solutions

  1. The dumbest solution would be just not use webhooks. Jenkins can be configured to poll a certain repository to check if there are some new PRs.

  2. More elegant way is somehow to pass webhooks inside corporate VPN.

I’m not keen on polling and prefer “push” over it. I started to search if there are some services that solve my problem. As I expected I’m not the only one with such problem and some companies have implemented so-called webhook forwarders. I found at least three such services:

One thing I should mention that Jenkins itself cannot connect to a webhook forwarder. There should be one more piece in the route. All these services also provides clients that connect to the server and then replay webhooks inside corporate network. Consider the following diagram:

diagram 1

diagram 1

I chose smee.io because both server and client are free and open source unlike other two. I created a route in smee.io and provisioned their client in our Openshift instance. It looked that the problem was solved.


I was happy until I was need to create another webhook forwarder route. The issue is smee.io client can forward webhooks only between one source and destination pair. It means that for every new webhook I would need to provision a new smee client. That looks a bit overhead for such simple task. Moreover the client is written in javascript :). Not a big deal but again for such simple task using this programming language is suboptimal.

I decided to write my own smee.io client implementation with two key features:

  1. It must be distributed as a single binary without any dependencies.

  2. One running instance of the client must forward multiple source-destination pairs.

Enter Xakac

This is how I came up to idea to create xakac. A small utility written on golang that replays webhooks which were sent by smee.io server. Golang perfectly fits to this task. Each source-destination pair works in a separate goroutine and source code is compiling into one small executable file without dependencies.

You may ask what does xakac mean? It’s a transliteration of russian word хакас (khakas). It’s native people of the place where I came from. By the way you can find the code here:


Known issues

It was a good exercise for me and my xakac works fine except one thing. Sometimes connection between client and server can suddenly be closed and in the logs I can see this error:

unexpected EOF

I need to figure out how to handle this situation and automatically reconnect a failed route.