MeshCentral — Deep System Reference
MeshCentral is an open-source, Apache-2.0-licensed remote monitoring and management platform originally created by Ylian Saint-Hilaire. The current generation was rewritten in 2017 using Node.js, WebSockets, and WebRTC, replacing a legacy architecture. It provides remote desktop, remote terminal, file transfer, hardware and software inventory, and system monitoring. The server runs on Node.js; the native agent (MeshAgent) is written in C with an embedded JavaScript runtime (DukTape) that executes the MeshCore management module pushed by the server. The platform is designed to be self-hosted, and the entire server-agent protocol is documented and reproducible from the open-source codebase. This reference covers the binary agent protocol in precise technical detail and documents the three CyberSector projects built directly on top of it.
[>_] Protocol Architecture
1.1 — Transport Layer
Agents connect to the MeshCentral server over a persistent WebSocket connection secured with TLS (WSS — WebSocket Secure) to the path /agent.ashx. All data is binary-framed; the agent sets binaryType = "arraybuffer" and all messages are transmitted as raw binary buffers rather than text frames. The underlying TLS layer encrypts all traffic end-to-end.
Because the transport is a standard WebSocket over TLS, the agent connection is structurally identical to a browser WebSocket connection. This is a deliberate design decision: it allows the full stack of web infrastructure — Nginx reverse proxies, load balancers, CDN edge nodes — to sit in front of a MeshCentral deployment without any special protocol handling. It also means a browser tab can open the same WebSocket connection a native binary agent uses, which is the foundational observation behind the CyberSector Browser Agent.
1.2 — Binary Protocol and Integer Encoding
The agent protocol is a structured binary protocol with no text framing or delimiter characters. All integer fields are big-endian — this is non-negotiable and is confirmed by the agent source code's u16 and u32 helper functions, which write the most-significant byte first. Command IDs are 16-bit unsigned integers (uint16). Length fields for certificates and hostnames are uint16. Fields for info version, agent ID, agent version, platform type, and capabilities are 32-bit unsigned integers (uint32). Messages are raw binary buffers concatenated in a defined order with field boundaries determined entirely by fixed field sizes — there are no length-prefixed frames at the outer message level and no delimiters between fields.
1.3 — Authentication Command Set
The binary protocol uses a numbered command space. Commands 0–9 are the authentication phase; commands 10 and above are post-authentication and require the agent to already be authenticated before the server will process them.
| Command | ID (hex) | ID (dec) | Direction | Phase |
|---|---|---|---|---|
| AUTH_REQUEST | 0x0001 | 1 | Both | Auth |
| AUTH_VERIFY | 0x0002 | 2 | Both | Auth |
| AUTH_INFO | 0x0003 | 3 | Agent → Server | Auth |
| AUTH_CONFIRM | 0x0004 | 4 | Server → Agent | Auth |
| CORE_MODULE | 0x000A | 10 | Server → Agent | Post-auth |
| CORE_HASH | 0x000B | 11 | Agent → Server | Post-auth |
| CORE_OK | 0x0010 | 16 | Server → Agent | Post-auth |
AUTH_CONFIRM (0x0004) and CORE_OK (0x0010) are entirely distinct commands at different protocol phases. AUTH_CONFIRM confirms the authentication handshake. CORE_OK (decimal 16, not 10) confirms the MeshCore module state and is a post-authentication command. These are not interchangeable.
1.4 — The Full Authentication Handshake
The following is the exact, correct protocol flow. Step order is fixed — sending AUTH_INFO before AUTH_VERIFY, for example, will cause the server to reject or ignore the message.
The serverHash is a SHA-384 hash of the server's TLS public key derived from the WebSocket TLS connection the agent just established. The agentNonce is 48 bytes of cryptographically random data. The agent sends this first, before the server sends anything.
The server sends its own nonce. Both directions use command ID 0x0001 — this is not called AUTH_RESPONSE in the protocol.
The critical authentication message. The agent sends its DER-encoded X.509 certificate followed by an RSA signature. The signature is computed over the 144-byte concatenation: serverHash (48) ‖ serverNonce (48) ‖ agentNonce (48). The algorithm is RSASSA-PKCS1-v1_5 with SHA-384 over a 3072-bit RSA keypair, producing a 384-byte signature. This proves the agent holds the private key for its certificate and that both sides share the correct connection context.
The server sends its own certificate and signature for mutual verification.
The agent reports its metadata: the 48-byte MeshID from its .msh provisioning file, its hostname, platform type, and capabilities bitmask. This message must be sent only after AUTH_VERIFY has been sent.
Receipt of AUTH_CONFIRM indicates the server has accepted the agent as authenticated. The agent is now a legitimate managed node in the server's device list, visible in the MeshCentral UI.
The server may request the agent's MeshCore hash to determine whether it needs to push an updated MeshCore JavaScript module.
The agent responds with its current MeshCore hash, or an empty payload if it has no MeshCore loaded.
The server confirms the MeshCore state is acceptable. This is decimal 16, hex 0x0010 — distinct from CORE_MODULE (0x000A, decimal 10).
1.5 — Agent Identity: Certificate and NodeID
Each agent has a persistent identity based on an RSA keypair. The agent generates a 3072-bit RSA keypair and issues itself a self-signed X.509 certificate. The certificate is DER-encoded (binary ASN.1) for transmission in the AUTH_VERIFY message.
The NodeID — the unique identifier for the device in MeshCentral's database — is derived deterministically from the public key, not from the certificate itself and not from the private key:
The result is a 64-character string that uniquely identifies the agent across all reconnections, provided the same keypair is reused.
1.6 — Server Hash Derivation
The serverHash field in AUTH_REQUEST is the SHA-384 hash of the TLS public key of the certificate the server presents on the WebSocket connection — specifically the public key component. This is distinct from the domain certificate fingerprint in the traditional sense, and it is also distinct from the ServerID field present in .msh provisioning files (which is the hash of the agent's own certificate).
When running behind a TLS-terminating reverse proxy such as Nginx, the agent must hash the public key of the proxy's certificate — the certificate actually seen on the WebSocket TLS handshake — not the backend MeshCentral server's internal certificate. This is a common source of configuration error.
1.7 — MeshCore
MeshCore is a JavaScript module that the MeshCentral server pushes to connected agents after authentication. It runs inside the agent's embedded DukTape JavaScript runtime — a lightweight ECMAScript engine compiled into the native MeshAgent binary. MeshCore provides higher-level management functionality on top of the binary protocol: remote desktop, terminal, file management, and scripting.
The core hash exchange (commands 0x000A, 0x000B) allows the server to selectively update MeshCore on individual agents without a full agent binary update. Because MeshCore runs on DukTape, which is an embedded C runtime, browser-based agents cannot execute MeshCore — the DukTape environment is not available in a browser context and cannot be polyfilled.
[◈] PRJ-001 — Browser Agent
Overview
The Browser Agent is a fully browser-native implementation of the MeshCentral agent protocol. It runs in a standard web browser tab: no Node.js, no compiled binary, no browser extension, no native wrapper. A standard webpage can register itself as a legitimate, first-class MeshCentral managed node. The project demonstrates that the MeshCentral binary protocol is fully implementable using only browser-standard APIs.
binaryType = "arraybuffer". All multi-byte integer fields are big-endian packed into ArrayBuffers.derSeq, derTlv, derBitString, etc.) build the certificate byte-for-byte in pure JS.Technology Choices and Constraints
The browser environment imposes specific constraints that require particular library choices. The native MeshAgent binary uses OpenSSL for RSA key generation, X.509 certificate creation, and signing. In the browser, all of these operations must be performed through the Web Crypto API (window.crypto.subtle), which is available natively in all modern browsers without any installation. The Web Crypto API supports RSASSA-PKCS1-v1_5 with SHA-384, which is the exact signing algorithm required by the MeshCentral protocol.
The Web Crypto API does not natively produce X.509 certificates — it can generate keypairs and sign data, but certificate construction requires ASN.1 DER serialization. Rather than pulling in a third-party library, the browser agent uses a minimal hand-written DER encoder: a small set of pure JS helper functions (derSeq, derTlv, derBitString, derInt, derUtf8, derGenTime, etc.) that build ASN.1 primitives and compose them into a fully valid self-signed X.509 certificate. The certificate is byte-for-byte DER-compatible with what the server expects, with no external dependencies.
Key Generation
Key generation uses window.crypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 3072, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "SHA-384" } }, true, ["sign", "verify"]). This takes approximately 200–500ms in a browser context. The private key remains in browser memory and is never serialized or transmitted.
NodeID Derivation
The public key is exported from Web Crypto in SPKI format (subtle.exportKey("spki", keyPair.publicKey)), SHA-384 is computed over the resulting ArrayBuffer (subtle.digest("SHA-384", spkiBuffer)), the result is base64-encoded, and MeshCentral's character substitutions are applied (+→@, /→$, = removed). This matches native MeshAgent NodeID derivation exactly.
Handshake Implementation
The browser opens a WebSocket to wss://<server>/agent.ashx with binaryType = "arraybuffer". It immediately sends AUTH_REQUEST (0x0001), then — upon receiving the server's AUTH_REQUEST — constructs and sends AUTH_VERIFY (0x0002) with the DER certificate and a signature over serverHash ‖ serverNonce ‖ agentNonce. AUTH_INFO (0x0003) follows after the server's AUTH_VERIFY is received. The agent waits for AUTH_CONFIRM (0x0004), at which point it is a registered, authenticated device in MeshCentral's device list, visible in the UI exactly like any native agent.
When the server sends CORE_MODULE (0x000A) requesting the MeshCore hash, the browser agent responds with CORE_HASH (0x000B) and an empty payload, indicating it has no MeshCore. This is correct behavior for a minimal agent — MeshCore execution is not implemented because it requires the DukTape embedded runtime. The agent maintains the WebSocket connection with keepalive pings approximately every 2 minutes, matching native agent behavior.
Implemented and Not Implemented
- RSA-3072 keypair generation via Web Crypto API
- Self-signed X.509 certificate (DER) via custom vanilla JS ASN.1/DER encoder
- NodeID derivation from SPKI DER public key
- Full binary authentication handshake (steps 1–6)
- CORE_MODULE detection and CORE_HASH response
- Persistent WebSocket connection with keepalive
- Protocol-accurate big-endian message encoding
- Browser canvas desktop streaming
- Remote terminal execution
- File transfer
- MeshCore execution (requires DukTape runtime)
- Persistent identity storage (IndexedDB optional)
Deployment
Fully static HTML — no build step, no serverless functions. Deployable to Vercel, Netlify, or GitHub Pages. HTTPS is required because the Web Crypto API is only available in secure contexts (window.isSecureContext === true).
Server Configuration
Compatible with MeshCentral ≥ 0.9.x. The server configuration option ignoreAgentHashCheck = true is available for development and testing, bypassing server hash verification. In production, the server hash in AUTH_REQUEST must match the SHA-384 of the TLS public key of the WebSocket endpoint the browser connects to — if behind a reverse proxy, this is the proxy's TLS public key, not the backend server's.
This implementation proves that MeshCentral's agent protocol — a protocol designed for native compiled binaries — is fully specifiable and reproducible from documentation and source code alone, without any binary tooling, and that the Web Crypto API provides sufficient cryptographic primitive access for strong agent identity.
[◈] PRJ-002 — React2API
Overview
React2API is a two-part system: a Node.js REST API that runs alongside MeshCentral on the same server, and a React/Next.js frontend operator console that consumes it. Together they replace the default MeshCentral web UI for operator workflows, providing real-time device status, group orchestration, and a clean JSON/REST interface that mobile clients and automation scripts can consume directly.
MeshCentral's native UI is a full-stack server-rendered application. Direct WebSocket access to /control.ashx — the user-facing management WebSocket — requires handling MeshCentral's proprietary JSON message format and maintaining a stateful WebSocket session. React2API abstracts this behind a stateless REST/JSON API layer, making it straightforward to build any frontend or automate management tasks from scripts. JWT authentication makes the API stateless and portable across serverless environments.
The REST API Layer
The API is a Node.js application (server.js) that runs on localhost:3001 and is reverse-proxied by Nginx at the /api/ path prefix, keeping MeshCentral's own UI accessible at the root. It uses both meshctrl.js and libmeshctrl.js — two distinct files that ship with every MeshCentral installation — as its backend engines. meshctrl.js is the command-line control tool located at node_modules/meshcentral/meshctrl.js, invoked as a subprocess for operations where parsed text output is sufficient. A separate utility module, utils/parseDevices.js, converts meshctrl's line-oriented text output into structured JSON for API responses.
libmeshctrl.js
libmeshctrl.js is a JavaScript library file bundled within MeshCentral's npm package itself — found at node_modules/meshcentral/libmeshctrl.js in a standard MeshCentral installation. It provides a programmatic async API over MeshCentral's WebSocket management channel (/control.ashx) for device listing, user management, and group operations. It is distinct from meshctrl.js (the command-line tool) and distinct from the separately maintained github.com/Ylianst/LibMeshCtrl project — the React2API implementation uses the version bundled with MeshCentral itself, not the external project.
Authentication Flow
A client POSTs credentials to /api/mobile-auth. The API validates credentials against MeshCentral via meshctrl, issues a signed JWT (Authorization: Bearer <token>), and returns it. All subsequent API calls include this token in the Authorization header, validated by jwtMiddleware.js on every protected route. The JWT secret is set via the JWT_SECRET environment variable. Token expiry is 3600 seconds. On the React side, tokens are stored in localStorage for session persistence across page reloads.
After receiving a JWT, the login flow immediately calls /api/devices to validate the session. MeshCentral returns [{}] — an array containing one empty object — for invalid credentials rather than an error status code. The API and React client both treat a devices response of [{}] as an authentication failure and surface an error to the user.
API Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /api/mobile-auth | Authenticate, receive JWT |
| GET | /api/devices | List all devices (ID, name, icon, conn, pwr, group) |
| GET | /api/users | List users |
| POST | /api/users | Add user |
| DELETE | /api/users/:userid | Remove user |
| GET | /api/device-groups | List device groups |
| POST | /api/device-groups | Create device group |
| DELETE | /api/device-groups/:groupId | Remove device group |
| POST | /api/device-groups/:groupId/users | Add user to group |
| DELETE | /api/device-groups/:groupId/users/:userid | Remove user from group |
Nginx Integration
The reverse proxy configuration forwards /api/ to http://127.0.0.1:3001/ while MeshCentral's own UI continues serving from http://127.0.0.1:8443 at the root. WebSocket upgrade headers are forwarded. proxy_read_timeout and proxy_send_timeout are set to 360 seconds to accommodate long-running management operations.
React/Next.js Frontend
The frontend is a Next.js application deployed to Vercel. It includes a login page (app/page.tsx), a dashboard (app/dashboard/page.tsx), and a component library: LoginForm.tsx, ServerSettings.tsx, DeviceList.tsx, and DeviceCard.tsx. The server URL is configurable at runtime via ServerSettings.tsx, persisted in localStorage, making the frontend capable of targeting multiple MeshCentral deployments without a rebuild. The dashboard displays device name, ID, group, connection status, and power state for every device visible to the authenticated user.
[⬡] PRJ-007 — Auto-Config
Overview
MeshCentral Auto-Config is a Node.js automation toolchain for provisioning MeshCentral server deployments from scratch. It reduces the initial setup of a production MeshCentral instance — domain configuration, agent group creation, user provisioning, and server parameter tuning — from a multi-hour manual process to a scripted, repeatable operation that can be version-controlled and re-run.
What It Automates
The toolchain covers scripted server configuration (writing and validating config.json), domain and TLS certificate configuration, agent device group creation and naming, user account provisioning and role assignment, and agent group membership management. These operations are performed programmatically via meshctrl.js — the command-line tool bundled with MeshCentral — executed as a subprocess with structured argument passing. Output is parsed and validated before the next step executes, so configuration errors surface immediately rather than silently persisting in a partially-configured state.
Repeatability
The toolchain is designed to be idempotent where possible — running it against an already-configured server will not create duplicate groups or users. Configuration state is version-controlled, meaning a deployment can be reproduced exactly from a configuration file checked into source control. This makes it suitable for infrastructure-as-code workflows and multi-instance deployments.
Relationship to the Broader Stack
Auto-Config is the provisioning layer that brings a MeshCentral server to the state where React2API and the Browser Agent can connect to it. Without Auto-Config, reaching that state requires navigating the MeshCentral web UI manually for each deployment. With it, a new server instance is ready for programmatic interaction in minutes, with device groups, user accounts, and server parameters configured from a single source-controlled specification.
[◻] MeshCentral in the CyberSector Stack
The three projects form a coherent vertical stack over MeshCentral. Auto-Config provisions the server and establishes device groups. The Browser Agent demonstrates that any browser-accessible device can be enrolled as a managed node without distributing a binary. React2API provides the operator interface for managing the resulting fleet — viewing status, organizing groups, and managing users — through a clean REST interface that is more accessible to mobile clients and automation scripts than MeshCentral's native WebSocket management protocol.
MeshCentral itself sits at the center as the source of truth for device identity, session management, and MeshCore distribution. The CyberSector work does not modify MeshCentral's server code; it operates entirely through documented protocols and published interfaces: the binary /agent.ashx handshake for the Browser Agent, meshctrl.js and libmeshctrl.js for Auto-Config and the React2API backend, and the /control.ashx WebSocket management channel for the React2API operator interface.
The browser-native approach to agent enrollment has specific practical implications. Because the Browser Agent runs in a tab, any device with a modern web browser — regardless of operating system, architecture, or the ability to execute unsigned code — can be enrolled into a MeshCentral-managed fleet. The identity persists across page reloads if the keypair is stored in IndexedDB; without persistence, the agent registers as a new node on each session. This is a deliberate tradeoff, and the architecture is designed to make persistence opt-in rather than default.
The entire stack is production-deployable. React2API's API layer and React frontend are live on Vercel and a VPS respectively. The Browser Agent is a deployable static site. Auto-Config runs against real MeshCentral installations. None of these are proof-of-concept scaffolding — they are operational implementations built to run.
[>_] Technical Reference
Authentication Commands
| Command | ID (hex) | ID (dec) | Direction | Phase |
|---|---|---|---|---|
| AUTH_REQUEST | 0x0001 | 1 | Both | Auth |
| AUTH_VERIFY | 0x0002 | 2 | Both | Auth |
| AUTH_INFO | 0x0003 | 3 | Agent → Server | Auth |
| AUTH_CONFIRM | 0x0004 | 4 | Server → Agent | Auth |
| CORE_MODULE | 0x000A | 10 | Server → Agent | Post-auth |
| CORE_HASH | 0x000B | 11 | Agent → Server | Post-auth |
| CORE_OK | 0x0010 | 16 | Server → Agent | Post-auth |
Cryptographic Parameters
| Parameter | Value |
|---|---|
| RSA key size | 3072 bits |
| Signature algorithm | RSASSA-PKCS1-v1_5 |
| Hash algorithm | SHA-384 |
| Signature size | 384 bytes |
| Nonce size (agent) | 48 bytes |
| Nonce size (server) | 48 bytes |
| Server hash size | 48 bytes (SHA-384 of TLS public key) |
| Signed payload | serverHash ‖ serverNonce ‖ agentNonce = 144 bytes (server hash first) |
| NodeID source | SHA-384 of SPKI DER public key |
| NodeID encoding | Base64 with +→@, /→$, = removed |
| Integer byte order | Big-endian (MSB first) |
| Certificate encoding | DER (binary ASN.1) — self-signed X.509 |
Endpoints
| Endpoint | Protocol | Purpose |
|---|---|---|
| /agent.ashx | WSS (binary) | Agent authentication and control channel |
| /control.ashx | WSS (JSON) | User/operator management channel (libmeshctrl.js) |
| /api/mobile-auth | HTTPS POST | React2API JWT authentication |
| /api/devices | HTTPS GET | React2API device list |
⬡ Live Browser Agent Demo
The MeshCentral Protocol Demo is a self-contained, browser-native simulation of the full agent lifecycle —
RSA-3072 identity generation, binary authentication handshake over /agent.ashx, KVM relay tunnel
establishment, and real-time JPEG tile streaming. Everything runs inside this server with no external connections.
The demo runs on its own dedicated page with a live protocol flow visualizer, agent computer canvas, server viewer canvas, and control buttons for refresh, pause/resume, and text injection.