Show HN: Hotcore – Reverse proxy configured with command, not config files

4 months ago 8

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

  • Painless install
  • Zero configuration
  • Out of the box HTTPS

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

  • HoTCo:RE is installed as a systemd service:
    • Starts automatically on boot
    • Restarts automatically if it crashes (though it shouldn’t)
  • SSL certificates are managed automatically:
    • Provisioned via Let’s Encrypt
    • Renewed when needed
  • WebSocket connections work out of the box!

Advantages of HoTCo:RE over other solutions:

  • Painless setup
  • No config files!
  • Programmatic interface!
  • Most importantly, a cool name!

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

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.

  • You can have your webapp send the add message on startup
  • You can have your deploy script run hotcore add ..
  • You can dynamically add domain mappings at anytime!

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

  • Version number
  • Health Check: is the server running properly?
  • Current domain mappings

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.

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.

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

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:

  • Network download
  • SystemD config

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:

  • Create a user hotcore if it does not exist
  • Create directory /opt/hotcore, if it does not exist, and assign it to user hotcore
  • Install the program binary to /opt/hotcore/bin
  • Provision capability to listen on port 80 and 443 without root permissions using setcap CAP_NET_BIND_SERVICE=+eip.
  • Unblock ports 80 and 443 from the system firewall. Supported firewalls:
    • ufw
    • firewall-cmd
  • Install a symlink to /usr/local/bin/
  • Create a system wide SystemD service:
    • Service runs under non-root user hotcore
    • Service is started immediately
    • Configured to start on system boot
    • Configured to restart if crashed

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.

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”.

  • You still need to run sudo ./hotcore install for the local build.
  • HTTPS support requires mkcert. If it’s not detected, it will fall back to HTTP.
  • No special hotcore user is created.

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.

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()

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.

Read Entire Article