HoTCo:RE

The easiest, simplest way to serve multiple domains from the same server

Introduction / Motivation

HoTCo:RE is an HTTP(S) reverse proxy that routes requests based on the domain name.

You have multiple web apps, each listening on a different port, but you want to serve them all from the same machine, and you want each one to respond to its own domain name.

App A is listening on port 8080, but you want to serve it on https://a-app.com.

App B is listening on port 9000, but you want to serve it on https://b-app.com.

You’ve got your DNS entries set up for the domain names to point to your server. Now what?

You could set up something like nginx, HAProxy, or Caddy.

You can even ask ChatGPT or Claude to guide you through the process.

Or you can just use HoTCo:RE

Quick Start

Step 1: Install

curl -fsSL https://judi.systems/hotcore/install.sh | bash

Step 2: Add domain mappings:

hotcore add a-app.com 8080
hotcore add b-app.com 9000

Done

That’s it! You can now access your apps at https://a-app.com and https://b-app.com.

Notes

Advantages of HoTCo:RE over other solutions:

You can have peace of mind; one less thing to worry about in your infrastructure!

Philosophy

HoTCo:RE is part of our effort to make self-hosting easy and painless.

That’s why it is designed to be low touch & hands off; just fire and forget.

Web infrastructure is already very complicated. Complex systems cause headaches. Every piece in the system is a potential point of failure.

The request routing engine should be mostly invisible, just like the kernel, or the TCP/IP stack; you never have to think about them.

Small self-contained binaries are much more preferred to complex systems with external dependencies. We don’t like to ship things as “Docker” images or scripts that require the installation of an interpreter and package manager.

We don’t like editing config files because it’s difficult to automate reliably. It’s also easy to make syntax errors but difficult to report on them.

Being driven by commands, whether on the CLI or UDP messages, means it’s easy to automate.

CLI Interface

You can invoke the command hotcore from the command line. It will inspect the current status and report to you:

The hotcore command also allows you to add, remove, and list domain mappings.

# Add a domain to port mapping
hotcore add example.com 5665

# Remove a domain to port mapping
hotcore remove example.com

# List all domain to port mappings
hotcore list

For a full list of commands, run hotcore help.

Programmatic Command Interface

You can programmatically control HoTCo:RE from your web app, you send a UDP message to port 40608.

The commands are similar to the ones you can send on the command line; they are plain strings, with no newline character at the end.

add example.com 5665

remove example.com

list

HoTCo:RE does not listen to - and will not accept - outside requests. Only programs running on the same machine can send messages via this interface.

For the full list of UDP messages accepted, run hotcore help.

Download

Latest release: v0.1-beta2

Binary: https://judi.systems/hotcore/hotcore-linux-amd64-v0.1-beta2.tar.xz

Source: https://judi.systems/hotcore/hotcore-src-v0.1-beta2.tar.xz

Installation

Download the latest binary and run it with the install sub-command with root permissions.

As mentioned in the quick start section, we provide a bash script that downloads the latest version and installs it.

$ curl -fsSL https://judi.systems/hotcore/install.sh | bash

Please DO NOT automate the download into your CI/CD pipeline.

The whole thing should not take more than a few seconds. The only bottlenecks:

When it completes, the latest version of HoTCo:RE will be installed as a system service, and it will also already be up and running.

The install command is idempotent: it’s safe to call multiple times.

It’s also safe to call even if you have an older version installed; it will just upgrade it.

Here’s what the install command does:

As we alluded to in the Philosophy section, HoTCo:RE is a a self-contained binary. It’s not a wrapper around some other tool.

Building from source

HoTCo:RE is implemented in Go.

Link to download the latest release’s source code is provided above.

The source code is vendored, you should be able to build it as-is without downloading any packages from the internet.

Make sure to pass the -tags=release argument.

go build -tags=release .

For reference, this is the actual full build command we use:

GOWORK=off GOOS=linux GOARCH=amd64 go build -ldflags "-w -s" -tags=release -o $binpath .

Local mode

If you build this program without any build tags, it will run in “local dev mode”.

We recommend using domains of the form app.localhost, since they work out of the box, without you have to edit the system’s hosts file.

How to send UDP packets

Sending UDP packets is a very simple and effective way to send messages between processes running on the same machine.

Very lightweight, compared to a full blown JSON API.

Since UDP isn’t common in web development, here are examples of how to send a string message and wait for the response in popular languages.

Javascript (node.js)

const dgram = require('dgram');

function sendUDPMessage(port, message) {
    return new Promise(resolve => {
        const socket = dgram.createSocket('udp4');
        socket.on('message', (msg) => resolve(msg.toString()));
        const cleanup = () => { socket.close(); resolve(null) }
        socket.send(message, port, 'localhost', () => setTimeout(cleanup, 100));
    });
}

Python

import socket

def send_udp_message(port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(0.1)  # 100 ms
    try:
        sock.sendto(message.encode(), ('127.0.0.1', port))
        data, _ = sock.recvfrom(1024)
        return data.decode()
    except socket.timeout:
        return None
    finally:
        sock.close()

Q & A

Why are you using UDP? I heard UDP is unreliable

We are sending short string messages on the local machine.

It’s as reliable as you can get.

What advantages does this have over nginx or Caddy?

nginx and Caddy can do a lot of things that HoTCo:RE does not do (and will not support).

However, neither of them is easy to control programmatically.

The most common use case for a reverse-proxy, which is to re-route requests based on the domain name, is very difficult to do programmatically in a reliable way.