Download Game! Currently 86 players. Last logged in:FeatherlightTailsGlauriArcanaut

Blitzer's Blog >> 73060

Back to blogs index
Posted: 17 Oct 2026 18:09 [ permalink ]
This is a fantastic application of decentralized architecture. You are
describing a masterless High Availability (HA) cluster with automatic
failover, built entirely on top of client-side nodes.

To answer your specific question about the relay: No, the relay should not
keep track of engines or announce when one drops.

If you make the relay responsible for tracking who is online and who owns
what, it becomes a "smart" central server, breaking the KISS principle and
your masterless design. Instead, we can achieve flawless failover using a
Watchdog / Dead Man's Switch pattern purely through the VFS, requiring zero
changes to your dmz_relay.js.

Here is how you distribute the state and handle serverless failover using the
"Everything is a file" philosophy.

1. The Heartbeat Registry (VFS Lock Files)

When an LPC script calls the new efun set_heart_beat(2), the local JavaScript
engine claims ownership of that heartbeat by creating a state file in the
mesh.

/ Path: /mesh/proc/heartbeats/monster#456.json { "owner_engine":
"chromium_tab_1", "interval_ms": 2000, "last_tick": 1788789727548 } 

2. The Distributed "Dead Man's Switch"

Every connected engine (whether it's a browser tab or a Node.js daemon)
constantly watches the /mesh/proc/heartbeats/ directory.

The Owner's Job: chromium_tab_1 runs the LPC heart_beat() function every 2
seconds. Every time it finishes, it updates the "last_tick" timestamp in the
VFS file to prove it is still alive.

The Watchers' Job: The other connected engines passively observe that file. If
an engine notices that Date.now() - last_tick is greater than the interval
plus a grace period (e.g., 4000ms), it realizes chromium_tab_1 has crashed or
disconnected.

The Takeover: The first watcher to notice the dead switch instantly overwrites
the file, changing "owner_engine" to its own ID. It is now the authoritative
host for that specific object's heartbeat.

3. Hydrating the State (Serverless Spare Capacity)

If Node_Engine_2 takes over a heartbeat for a monster that was running in
chromium_tab_1, how does it know the monster's current HP and coordinates?

You have already built the foundation for this! In our previous step, we
started dumping live clone variables to /mesh/proc/instances/.

When Node_Engine_2 claims the heartbeat lock, it performs a Hydration
Sequence:

It reads /mesh/proc/instances/monster#456.json to get the latest variables
(HP, inventory, coordinates).

It calls load_object() on the blueprint (/cmds/monster.c) to JIT-compile the
logic if it isn't already in RAM.

It creates a local clone, injects the variables from the VFS into the clone's
memory, and begins executing the heart_beat() loop.

4. Implementation: The HeartbeatManager

Here is a lightweight, drop-in JavaScript class you can add to your
template.html (or harness.js) to handle this entire distributed failover
system autonomously.

class DistributedHeartbeat { constructor(engineId, vfs, rt) { this.engineId =
engineId; // e.g., 'browser_' + Math.random() this.vfs = vfs; // Your
window.__VFS / dmzSocket wrapper this.rt = rt; // The LPC Runtime
this.activeBeats = new Map(); // Start the Watchdog Loop setInterval(() =>
this._watchdogTick(), 1000); } /** Called by the LPC efun set_heart_beat() */
setHeartBeat(objectId, intervalMs) { if (intervalMs === 0) { this._stopHeartBea
t(objectId); return; } // Claim ownership in the VFS const payload = {
owner_engine: this.engineId, interval_ms: intervalMs, last_tick: Date.now() };
this._writeVfs(`/mesh/proc/heartbeats/${objectId}.json`, payload); // Start
local execution this.activeBeats.set(objectId, setInterval(() => {
this._executeTick(objectId, intervalMs); }, intervalMs)); } _executeTick(object
Id, intervalMs) { // 1. Call the LPC function const target = this.rt.master_obj
ects[objectId]; if (target) { try { this.rt.call_other(target, "heart_beat");
} catch (e) { console.error(`Heartbeat crashed on ${objectId}:`, e); } } // 2.
Update the Dead Man's Switch in VFS const payload = { owner_engine:
this.engineId, interval_ms: intervalMs, last_tick: Date.now() };
this._writeVfs(`/mesh/proc/heartbeats/${objectId}.json`, payload); }
_watchdogTick() { const hbDir = 'mesh/proc/heartbeats/'; Object.keys(this.vfs).
forEach(path => { if (!path.startsWith(hbDir)) return; try { const data =
JSON.parse(this.vfs[path]); // If we own it, ignore it (our setInterval
handles it) if (data.owner_engine === this.engineId) return; // Check if the
owner is dead (missed 2 beats) const isDead = (Date.now() - data.last_tick) >
(data.interval_ms * 2); if (isDead) { const objectId = path.replace(hbDir,
'').replace('.json', ''); console.log(`[HA-Cluster] Node ${data.owner_engine}
flatlined. Taking over ${objectId}!`); // Hydrate state and claim the
heartbeat this._hydrateAndTakeover(objectId, data.interval_ms); } } catch
(err) { // Ignore parse errors on partial VFS writes } }); } _hydrateAndTakeove
r(objectId, intervalMs) { // 1. Load
 the latest variables from the telemetry dump const statePath =
`mesh/proc/instances/${objectId}.json`; if (this.vfs[statePath]) { const state
= JSON.parse(this.vfs[statePath]); // Logic to inject state.variables into
this.rt.master_objects[objectId] goes here } // 2. Claim it! (This triggers
our own setHeartBeat to start the loop) this.setHeartBeat(objectId,
intervalMs); } _writeVfs(path, data) { const payloadStr = JSON.stringify(data);
 this.vfs[path] = payloadStr; // In your current setup, also push to dmzSocket
here } } 

By relying strictly on timestamps in VFS files, you get a self-healing cluster
where any browser tab or headless Node instance can instantly absorb the
computational load of a disconnected peer, keeping your MUD world ticking
seamlessly without a master server.