Feature · 02

Live console

Logs stream from the container's stdio over a single WebSocket per server, multiplexed across every connected client. Commands typed in the panel land directly in the JVM.

01

Handshake

Connect to /ws/console with a JWT in the query string. The gateway validates the token in handleConnection and disconnects on failure before any room is joined.

const ws = io("https://cexi.my.id", {
  path: "/ws/console",
  query: { token: localStorage.getItem("mafcloud.token") },
});

ws.emit("join", { serverId });
02

Replay buffer

The first event after join is history: the last 200 lines of the container's combined stdout/stderr, fetched via container.logs. This bridges the gap before the live attach stream takes over.

ws.on("history", ({ lines }) => term.write(lines));
ws.on("log",     ({ data })  => term.write(data));
03

Multiplexing

M.A.F Cloud holds at most one Docker attach stream per running container and emits its output to a Socket.io room (server:<id>). Joining as a second client increments a refcount; leaving decrements it; the stream is torn down when the count reaches zero. This avoids the multi-attach buffering quirks the Docker daemon has when several clients hold their own stream concurrently.

04

Commands

A command event is bounded at 1 KB and stripped of trailing newlines server-side, then a single \n is appended before the bytes are written to the attach stream's stdin half. Anything you would type at the server console works:say hello, op steve, stop.

ws.emit("command", { serverId, text: "say hello" });
05

A note on Cloudflare

Cloudflare's free plan caps proxied WebSocket idle time at roughly 100 seconds. For a hosting panel where a session might stay open while you read logs, this manifests as the console silently dropping every couple of minutes. M.A.F Cloud's recommended setup is to keep the API hostname on DNS-only mode (grey cloud), so traffic hits nginx directly and the proxy timeout governs (default 1h).