Skip to content
AquadactylAquadactyl
API reference

Console WebSocket

Obtain a short-lived token, authenticate with Wings and reuse the panel's existing console connection in extensions.

The panel grants access to a Wings WebSocket. First call GET /api/client/servers/{server}/websocket with a Client key or panel session. The user needs websocket.connect; server access and state checks still apply.

{
  "data": {
    "token": "SHORT_LIVED_JWT",
    "socket": "wss://node.example.com/api/servers/SERVER_UUID/ws"
  }
}

Use the returned socket URL. It can point at a different node during a transfer. The JWT expires after 10 minutes and contains the server UUID and allowed user permissions. It is a Wings credential; never send your panel API key in a WebSocket message.

Wire protocol

Open the socket and authenticate:

const socket = new WebSocket(socketUrl);
socket.addEventListener("open", () => {
  socket.send(JSON.stringify({ event: "auth", args: [token] }));
});
socket.addEventListener("message", ({ data }) => {
  const message = JSON.parse(data);
  console.log(message.event, message.args);
});

Messages use {"event":"event name","args":[...]}. Wait for auth success before requesting output. Common events used by the panel include:

DirectionEventUse
Client → WingsauthJWT in args[0]; also used to refresh the token.
Client → Wingssend logsRequest recent console output.
Client → Wingssend statsRequest resource stats.
Client → Wingssend commandConsole command in args[0]; requires console permission.
Client → Wingsset statePower action in args[0]; subject to action permissions.
Wings → Clientauth successAuthentication completed.
Wings → Clientconsole outputConsole text.
Wings → ClientstatsResource statistics, sent as a JSON string argument.
Wings → ClientstatusServer state.
Wings → Clienttoken expiring, token expiredObtain a new JWT from the panel and send auth again.
Wings → Clientjwt error, daemon errorAuthentication or daemon failure.
Wings → Clienttransfer statusTransfer lifecycle; the panel may reconnect to the target node.

Refresh tokens through the authenticated panel endpoint, rather than caching one indefinitely. Close the connection and remove listeners on unmount or logout. Use a bounded reconnect policy and handle permission revocation or terminal close codes instead of reconnecting forever.

Inside a Blueprint extension

Reuse ServerContext and the existing socket whenever possible. Attach a listener and remove the same listener in the effect cleanup:

import { useEffect } from "react";
import { ServerContext } from "@/state/server";

export default function ExampleListener() {
  const socket = ServerContext.useStoreState((state) => state.socket.instance);
  useEffect(() => {
    if (!socket) return;
    const onStatus = (status: string) => console.log("Server state:", status);
    socket.on("status", onStatus);
    return () => {
      socket.off("status", onStatus);
    };
  }, [socket]);
  return null;
}

Do not call socket.close() from this component's cleanup: the connection belongs to the surrounding server page. HTTP polling remains useful for simple resource cards; the Server Pulse example demonstrates it.

Sources: token controller, WebSocket client, connection lifecycle.