← All work
Case studyInfrastructure · Go · Remote execution

Centralized Fleet Control

One central application commanding many machines — without opening a single port on any of them. Two Go services make it work: a node agent on every machine that runs only Ed25519-signed tasks, and a WebSocket relay that holds each machine's outbound connection and carries commands and replies through Valkey.

Small on purpose: about 1,400 lines of Go between the relay and the agent. The interesting part is the contract between the three applications — who may sign what, how a machine is claimed, and what the relay promises so the central system never hangs.

Role
Design, engineering & coding
Domain
Remote execution · fleet management
Parts
Central app · Go relay · Go node agent
Status
Built in 2026 · internal product
The problem
A central system has to run work on a growing number of servers — and know which are reachable — without opening a port on any of them.
What was built
Morsalin designed and built a Go WebSocket relay and a Go node agent that runs only Ed25519-signed tasks addressed to its own machine.
The result
Built in 2026 for an internal product: zero inbound ports on any machine. Commands run in order per machine and in parallel across the fleet, and every command gets an answer.
3cooperating applications
0inbound ports on machines
~1.0klines of Go · agent
~370lines of Go · relay
4task types
60 sreply lifetime
20 spresence heartbeat
1setup call per machine
01The problem

Command every machine from one place — without letting anyone in.

01

One place, many machines.

A central system has to run work on a growing number of servers — deploy, configure, read and write files — and know which of them are reachable right now.

02

An open port on every server is a target.

Listening for commands means a firewall rule, a public address and a reason to trust whoever connects — on every machine.

03

Root access needs a narrow door.

Whatever runs commands as root should accept a small, well-defined set of tasks, and only from the one system allowed to send them.

02Three applications

One decides, one carries, one executes.

fig.02 — one central system, many machinesno inbound ports on any machine
Central applicationsigns each task (Ed25519)publishes its public keyswaits on one reply keypushreplyValkeycommand list per nodereply per command · 60 spresence per node · 30 sWebSocket relay · Goone worker per nodereplies routed by command IDheartbeat every 20 sMachine 1 · agentdials out, no portMachine 2 · agentdials out, no portMachine N · agentdials out, no portoutbound WebSocketInside each node agent› verifies every task’s Ed25519 signature› issuer, audience and node ID must match› shell as a chosen user, with a timeout› file reads and writes with owner and mode› replies over the same connectionprovisioningTrust transition1A built-in setup key accepts one setup call2The app registers node ID + per-node public key3Setup locks; only this node’s signed tasks run
01

The central application

The one system that decides what happens. It keeps a key pair per machine, signs every task for exactly one machine, pushes it into Valkey and waits on a reply key. It never opens a connection to a server.

02

The relay

A small Go service that holds every machine’s long-lived WebSocket. It moves commands out and replies back through Valkey and keeps each machine’s presence current. It holds no signing keys, so it cannot forge a task.

03

The node agent

A Go service on every machine. It dials out to the relay, reconnects on its own, and runs a task only after its signature and claims check out — then replies over the same connection.

03Life of a command

From the central system to one machine and back.

  1. 1

    Sign

    The central application builds a task for one machine and signs it with that machine’s Ed25519 key. The issuer, audience and the machine’s own ID are part of what is signed.

  2. 2

    Queue

    It pushes the signed task onto that machine’s command list in Valkey, then waits on a reply key named after the command.

  3. 3

    Deliver

    The relay’s worker for that machine pops the command and sends it down the machine’s open WebSocket.

  4. 4

    Verify and run

    The agent checks the signature against the central application’s published keys, checks that the claims name this machine, runs the task and sends the result back.

  5. 5

    Answer

    The relay matches the reply to its command ID and stores it for 60 seconds; the central application’s wait ends with the result — or with a clear failure if anything went wrong on the way.

04Adding a machine

Claimed once, locked for good.

A fresh machine trusts one key

A newly installed agent only knows a built-in setup key, and accepts exactly one setup call signed with it.

The central application claims it

That call registers the machine’s ID and a new per-machine Ed25519 public key generated by the central application.

Then it locks

Setup is disabled for good. From now on the machine runs only tasks signed with its own key and addressed to its own ID.

Keys can rotate

Over the relay, the agent verifies against the central application’s published key set and picks the key by ID, so keys change without touching the machine.

05Many machines at once

Parallel across the fleet, in order on each machine.

Who is online

Each machine has a presence flag that lives 30 seconds and is refreshed every 20. The central application reads it to know which machines it can reach right now.

In parallel, in order

Every machine has its own worker in the relay, so the fleet works in parallel — while commands for any one machine run strictly in order, so deployment steps never overtake each other.

A slow machine delays only itself

Because each queue is separate, one busy or slow server never holds up the rest of the fleet.

Reconnects are safe

A machine that reconnects replaces its old connection, and the old connection closing cannot mark the machine offline.

06The node agent

Four tasks, and nothing else.

shell_commandRuns a command as a chosen user — the agent drops to that user’s UID and GID before executing — with a timeout.
file_existsAnswers whether a path exists, without reading it.
read_fileReturns a file’s contents.
write_fileWrites base64 content with an optional owner, group and octal mode.
07The relay

What the central system can rely on.

Nobody waits forever

A failed delivery, a timeout or a disconnect still produces a failure reply, so the central application’s wait always ends.

Replies find their command

Pending replies are tracked by command ID; a late answer to a command that already timed out is dropped rather than misdelivered.

One writer per socket

Command delivery and heartbeats share a lock, because a WebSocket allows only one writer at a time.

A shared contract, not a shared codebase

The central application and the relay agree only on Valkey key names and message shapes, so either side can be redeployed independently.

08Key decisions

Every non-obvious choice, with its reason.

Machines dial out

No listening port, no firewall rule, and it works behind NAT.

A queue between the central app and the relay

The central application never holds a socket; the relay can restart without breaking the contract.

Keys stay with the central application

The relay only moves messages; compromising it cannot produce a valid task.

A key per machine

A task signed for one machine is useless on any other.

Claim once, then lock

A shared setup key can claim a machine exactly once; after that, only that machine’s own key works.

A small task vocabulary

Four task types are easier to reason about than a general remote shell API.

Serialise per machine, parallel across the fleet

Steps arrive in order on each machine; one slow machine cannot block the others.

Always answer, even on failure

A caller that can hang is worse than one that gets an error.

09Outcome

One system in charge, no doors left open.

Central control

One application can run work on any number of machines and knows which are online.

No open ports

Machines connect out; nothing on them listens for commands from the internet.

Signed, addressed tasks

Every task is signed for one machine and verified there before it runs.

Predictable delivery

In-order per machine, parallel across the fleet, and every command gets an answer.

My role

I designed, built and coded the system: the contract between the three applications, the per-machine signing and claiming model, the agent’s task set and execution rules, the relay’s queue contract and concurrency guarantees, and the central application’s side in Laravel. The relay ships as a static binary in a small container image, the agent as a systemd service.

Stack

Central
Laravel · per-machine Ed25519 keys · Valkey
Relay
Go · WebSocket · Valkey · Docker
Agent
Go · Ed25519 · JWT / JWKS · systemd
GoConcurrencyWebSocketValkey / RedisEd25519JWT / JWKSLaravelRemote executionFleet managementLinuxsystemdDockerDistributed systems

Built for an internal product — product and infrastructure identifiers are deliberately omitted.

Next

Managing a fleet from one place?

Get in touch →
© 2026 MD Morsalinbuilt with care · OSS-first · sustainable by design
lat: 23.81°Nlon: 90.41°Etz: UTC+6status: 200 OK