Remote Server Architecture in Craft Agents Using WebSockets and TLS

Craft Agents OSS implements a Node.js-based remote server that exposes a secure WebSocket endpoint (wss://) protected by TLS encryption, using X.509 certificates for HTTPS termination and the ws library for connection management.

The craft-ai-agents/craft-agents-oss repository provides a production-ready remote server architecture that enables real-time, bidirectional communication between AI agents and clients. This system combines TLS-encrypted HTTP servers with persistent WebSocket connections to deliver secure, low-latency messaging for command-line interfaces, Electron applications, and external integrations.

Architecture Overview

The remote server architecture follows a layered security model where Transport Layer Security (TLS) encrypts all traffic before WebSocket protocols handle the persistent connection logic. According to the source code, the implementation creates an https.Server instance that terminates TLS connections, then wraps this server with a WebSocket upgrade handler to support wss:// protocols.

The architecture consists of five primary components:

  1. TLS Termination – Standard HTTPS server creation using X.509 certificates configured via environment variables.
  2. Protocol Upgrade – HTTP to WebSocket promotion using the ws library or Bun's native WebSocket support.
  3. Authentication – JWT token validation passed via query strings during connection establishment.
  4. Message Routing – Central gateway dispatching messages between agents and connected clients.
  5. Client Bridges – CLI and Electron implementations that maintain secure WebSocket connections.

Core Components

TLS Termination and HTTPS Server Setup

In scripts/build-server.ts, the server initializes TLS by reading certificate files from paths defined in the environment. The implementation uses Node.js https.createServer() with key and cert options populated by fs.readFileSync() calls targeting process.env.TLS_KEY_PATH and process.env.TLS_CERT_PATH.

This HTTPS server serves as the foundation for the WebSocket layer, ensuring all subsequent WebSocket traffic travels through an encrypted tunnel. The .env.example file defines these required variables, allowing operators to specify production certificate paths or development self-signed equivalents.

WebSocket Upgrade Mechanism

The packages/server/src/index.ts file implements the protocol upgrade by instantiating a WebSocketServer from the ws library and passing the existing HTTPS server instance as the server option. This coupling means the WebSocket server listens for upgrade requests on the same TLS-protected port, automatically encrypting all WebSocket (WS) traffic as WebSocket Secure (WSS).

When a client connects to wss://<host>:<port>, the TLS handshake occurs first, followed by the HTTP upgrade request that promotes the connection to the WebSocket protocol.

Message Routing Gateway

Once upgraded, connections are managed by packages/messaging-gateway/src/gateway.ts. This module authenticates incoming clients by extracting and validating JWT tokens from the URL query string (?token=<jwt>), then registers each session for bidirectional message flow.

The gateway handles JSON-encoded message serialization, routing commands from connected clients to the appropriate agent sessions and forwarding agent responses back to the originating client. This central hub maintains the mapping between WebSocket connections and active agent processes.

Client Bridge Implementations

Two primary client implementations demonstrate the architecture:

CLI Client (apps/cli/src/client.ts): Establishes wss:// connections using the ws library, implements reconnection logic for dropped connections, and serializes commands to JSON before transmission.

Electron Bridge (apps/electron/resources/bridge-mcp-server/index.js): Mirrors the CLI implementation within the Electron renderer process, creating a secure bridge between the desktop UI and the remote agent server using the browser's native WebSocket constructor.

Both clients construct connection URLs using environment variables for host, port, and authentication tokens, ensuring consistent secure connectivity across deployment scenarios.

Configuration and Deployment

Environment Variables

The .env.example file specifies the required TLS configuration:

  • TLS_CERT_PATH: Path to the X.509 certificate file (full chain)
  • TLS_KEY_PATH: Path to the private key file
  • SERVER_PORT: HTTPS/WSS listening port (typically 443)

Development Certificates

For local development, scripts/generate-dev-cert.sh generates self-signed X.509 certificates. This script creates certificate authority (CA) signed development materials that satisfy the TLS requirements without purchasing commercial certificates, allowing developers to test wss:// connections locally.

Docker Deployment

The Dockerfile.server containerizes the application for production, copying TLS certificates into the image or mounting them as volumes. The container runs the server process with access to the certificate paths, exposing the standard HTTPS port for WebSocket connections.

Implementation Examples

Starting the TLS-Enabled Server

import https from "https";
import { readFileSync } from "fs";
import { WebSocketServer } from "ws";

// Load TLS material from environment-defined paths
const server = https.createServer({
  key: readFileSync(process.env.TLS_KEY_PATH!),
  cert: readFileSync(process.env.TLS_CERT_PATH!),
});

// Attach WebSocket server to HTTPS instance
const wss = new WebSocketServer({ server });

wss.on("connection", (ws, req) => {
  // Extract JWT from query string for authentication
  const token = new URL(req.url!, `https://${req.headers.host}`).searchParams.get("token");
  // Validate token and bind session...
  ws.on("message", (msg) => handleMessage(ws, msg));
});

server.listen(process.env.PORT ?? 443);

CLI Client Connection

import WebSocket from "ws";

const ws = new WebSocket(
  `wss://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}?token=${process.env.JWT}`
);

ws.on("open", () => {
  console.log("🔗 Connected securely via WSS");
  ws.send(JSON.stringify({ type: "hello", payload: "client ready" }));
});

ws.on("message", (data) => {
  const msg = JSON.parse(data.toString());
  // Process server-side events and agent responses
});

Docker Deployment with TLS

docker build -f Dockerfile.server -t craft-agent-server .
docker run -d \
  -e TLS_CERT_PATH=/certs/fullchain.pem \
  -e TLS_KEY_PATH=/certs/privkey.pem \
  -p 443:443 \
  -v /my/certs:/certs \
  craft-agent-server

Summary

Frequently Asked Questions

How does Craft Agents handle TLS certificate configuration?

The server reads certificate paths from the TLS_CERT_PATH and TLS_KEY_PATH environment variables, loading the X.509 materials via fs.readFileSync() when creating the https.Server instance in scripts/build-server.ts. Operators can use self-signed certificates for development or production certificates from trusted CAs.

What WebSocket library does Craft Agents use?

The implementation primarily uses the ws library for Node.js environments, though it also supports Bun's native WebSocket implementation. In packages/server/src/index.ts, the WebSocketServer constructor receives the HTTPS server instance to handle secure WebSocket upgrades.

How do clients authenticate with the remote server?

Clients pass a signed JWT token in the WebSocket connection URL query string (e.g., wss://host:port?token=<jwt>). The messaging gateway in packages/messaging-gateway/src/gateway.ts extracts and validates this token during the connection event before allowing message exchange.

Can I run the server without TLS for local development?

While TLS is required for wss:// connections, the repository includes scripts/generate-dev-cert.sh to generate self-signed certificates automatically. This allows developers to test the full TLS + WebSocket stack locally without disabling encryption, ensuring the development environment matches production security configurations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →