Overview
The WebSocket API provides real-time bidirectional communication with Modrinth Hosting servers. It enables:
- Real-time console log streaming
- Server statistics monitoring (CPU, RAM, network)
- Power state change notifications
- Backup progress tracking
- Mod installation status updates
- Sending console commands
WebSocket connections are automatically authenticated using JWT tokens and support auto-reconnection with exponential backoff.
Connection Flow
The WebSocket client handles the complete authentication flow automatically:
1. client.archon.sockets.safeConnect(serverId)
↓
2. Fetches JWT token via archon.servers_v0.getWebSocketAuth(serverId)
↓
3. Opens WebSocket connection to wss://[url]
↓
4. Sends authentication message: { event: 'auth', jwt: token }
↓
5. Server responds with { event: 'auth-ok' }
↓
6. Connection ready - start receiving events
Connecting to a Server
import { GenericModrinthClient } from '@modrinth/api-client'
const client = new GenericModrinthClient({
token: 'your-auth-token'
})
// Connect to server WebSocket
await client.archon.sockets.safeConnect(serverId)
// Subscribe to events
const unsub = client.archon.sockets.on(serverId, 'log', (event) => {
console.log(`[${event.stream}] ${event.message}`)
})
// Clean up when done
unsub()
client.archon.sockets.disconnect(serverId)
Safe Connect Options
// Connect only if not already connected
await client.archon.sockets.safeConnect(serverId)
// Force reconnect even if already connected
await client.archon.sockets.safeConnect(serverId, { force: true })
Event Types
Log Events
Real-time console output from the server.
client.archon.sockets.on(serverId, 'log', (event) => {
console.log(`[${event.stream}] ${event.message}`)
})
Output stream: stdout or stderr
Stats Events
Server resource usage statistics, sent periodically.
client.archon.sockets.on(serverId, 'stats', (event) => {
const cpuPercent = event.cpu_percent
const ramUsedGB = event.ram_usage_bytes / (1024 ** 3)
const ramTotalGB = event.ram_total_bytes / (1024 ** 3)
console.log(`CPU: ${cpuPercent.toFixed(1)}%`)
console.log(`RAM: ${ramUsedGB.toFixed(2)}GB / ${ramTotalGB.toFixed(2)}GB`)
})
CPU usage percentage (0-100)
Current RAM usage in bytes
Total RAM available in bytes
Current storage usage in bytes
Total storage available in bytes
Network bytes transmitted
Power State Events
Notifications when server power state changes.
client.archon.sockets.on(serverId, 'power-state', (event) => {
console.log(`Server is now: ${event.state}`)
if (event.state === 'crashed') {
if (event.oom_killed) {
console.error('Server crashed due to out of memory')
}
console.error(`Exit code: ${event.exit_code}`)
}
})
Power state: running, stopped, starting, stopping, or crashed
Present when state is crashed - indicates if killed due to out of memory
Present when state is crashed - process exit code
Uptime Events
Server uptime information.
client.archon.sockets.on(serverId, 'uptime', (event) => {
const hours = Math.floor(event.uptime / 3600)
const minutes = Math.floor((event.uptime % 3600) / 60)
console.log(`Server uptime: ${hours}h ${minutes}m`)
})
Backup Progress Events
Track backup creation, restoration, or file operations.
client.archon.sockets.on(serverId, 'backup-progress', (event) => {
const percent = (event.progress * 100).toFixed(1)
console.log(`${event.task}: ${percent}% (${event.state})`)
if (event.state === 'done') {
console.log(`Backup ${event.id} completed`)
} else if (event.state === 'failed') {
console.error(`Backup ${event.id} failed`)
}
})
Task type: file, create, or restore
Task state: ongoing, done, failed, cancelled, or unchanged
Progress value from 0.0 to 1.0
Installation Result Events
Mod installation success or failure notifications.
client.archon.sockets.on(serverId, 'installation-result', (event) => {
if (event.result === 'ok') {
console.log('Mod installed successfully')
} else {
console.error(`Installation failed: ${event.reason}`)
}
})
Always "installation-result"
Installation result: ok or err
Error message (only present when result is err)
New Mod Events
Notification when a new mod is detected on the server.
client.archon.sockets.on(serverId, 'new-mod', (event) => {
console.log(`New mod detected: ${event.project_id}`)
console.log(`Version: ${event.version_id}`)
})
Filesystem Operation Events
Track long-running filesystem operations (e.g., archive extraction).
client.archon.sockets.on(serverId, 'filesystem-ops', (event) => {
event.all.forEach((op) => {
const percent = (op.progress * 100).toFixed(1)
console.log(`${op.op} (${op.id}): ${percent}%`)
console.log(`Files: ${op.files_processed}, Bytes: ${op.bytes_processed}`)
if (op.current_file) {
console.log(`Current: ${op.current_file}`)
}
if (op.state === 'failure-invalid-path') {
console.error(`Invalid path: ${op.invalid_path}`)
}
})
})
Array of filesystem operationsOperation type (currently only unarchive)
Number of bytes processed
Number of files processed
State: queued, ongoing, done, cancelled, failure-corrupted, or failure-invalid-path
Currently processing file
Invalid path that caused failure
ISO 8601 timestamp when operation started
Authentication Events
WebSocket authentication status notifications.
// Authentication successful
client.archon.sockets.on(serverId, 'auth-ok', (event) => {
console.log('WebSocket authenticated')
})
// Token expiring soon (handled automatically)
client.archon.sockets.on(serverId, 'auth-expiring', (event) => {
console.log('Auth token expiring, refreshing...')
})
// Authentication failed
client.archon.sockets.on(serverId, 'auth-incorrect', (event) => {
console.error('WebSocket authentication failed')
})
auth-ok, auth-expiring, or auth-incorrect
The client automatically handles auth-expiring events by fetching a new token and re-authenticating.
Sending Commands
Send console commands to the server.
// Send a single command
client.archon.sockets.send(serverId, {
event: 'command',
cmd: '/say Hello from the API!'
})
// Stop the server via console
client.archon.sockets.send(serverId, {
event: 'command',
cmd: '/stop'
})
// Give a player operator status
client.archon.sockets.send(serverId, {
event: 'command',
cmd: '/op PlayerName'
})
The server ID to send the command to
The console command to execute (include leading / for game commands)
Auto-Reconnection
The WebSocket client automatically reconnects on unexpected disconnections using exponential backoff:
- Base delay: 1 second
- Max delay: 30 seconds
- Max attempts: 10
- Backoff strategy: Exponential with jitter
// Check connection status
const status = client.archon.sockets.getStatus(serverId)
if (status) {
console.log('Connected:', status.connected)
console.log('Reconnecting:', status.reconnecting)
console.log('Reconnect attempts:', status.reconnectAttempts)
}
Manual Reconnection
// Disconnect and reconnect
client.archon.sockets.disconnect(serverId)
await client.archon.sockets.safeConnect(serverId)
// Force reconnect even if already connected
await client.archon.sockets.safeConnect(serverId, { force: true })
Complete Example: Server Console
Here’s a complete example building a server console interface:
import { GenericModrinthClient } from '@modrinth/api-client'
class ServerConsole {
constructor(serverId, authToken) {
this.serverId = serverId
this.client = new GenericModrinthClient({ token: authToken })
this.logs = []
this.stats = null
this.powerState = 'unknown'
}
async connect() {
// Connect to WebSocket
await this.client.archon.sockets.safeConnect(this.serverId)
// Subscribe to logs
this.unsubscribers = [
this.client.archon.sockets.on(this.serverId, 'log', (event) => {
this.logs.push({ stream: event.stream, message: event.message })
this.onLog?.(event)
}),
// Subscribe to stats
this.client.archon.sockets.on(this.serverId, 'stats', (event) => {
this.stats = event
this.onStats?.(event)
}),
// Subscribe to power state
this.client.archon.sockets.on(this.serverId, 'power-state', (event) => {
this.powerState = event.state
this.onPowerState?.(event)
}),
// Subscribe to backup progress
this.client.archon.sockets.on(this.serverId, 'backup-progress', (event) => {
this.onBackupProgress?.(event)
}),
// Subscribe to installation results
this.client.archon.sockets.on(this.serverId, 'installation-result', (event) => {
this.onInstallationResult?.(event)
})
]
console.log('Connected to server console')
}
sendCommand(cmd) {
this.client.archon.sockets.send(this.serverId, {
event: 'command',
cmd
})
}
async startServer() {
await this.client.archon.servers_v0.power(this.serverId, 'Start')
}
async stopServer() {
await this.client.archon.servers_v0.power(this.serverId, 'Stop')
}
async restartServer() {
await this.client.archon.servers_v0.power(this.serverId, 'Restart')
}
disconnect() {
// Unsubscribe from all events
this.unsubscribers?.forEach((unsub) => unsub())
// Disconnect WebSocket
this.client.archon.sockets.disconnect(this.serverId)
console.log('Disconnected from server console')
}
}
// Usage
const console = new ServerConsole('server-id', 'auth-token')
// Set up event handlers
console.onLog = (event) => {
console.log(`[${event.stream}] ${event.message}`)
}
console.onStats = (event) => {
const cpuPercent = event.cpu_percent.toFixed(1)
const ramGB = (event.ram_usage_bytes / (1024 ** 3)).toFixed(2)
console.log(`CPU: ${cpuPercent}% | RAM: ${ramGB}GB`)
}
console.onPowerState = (event) => {
console.log(`Power state changed: ${event.state}`)
}
// Connect and use
await console.connect()
// Send commands
console.sendCommand('/list')
console.sendCommand('/say Server managed via API')
// Control power
await console.startServer()
// Clean up when done
process.on('SIGINT', () => {
console.disconnect()
process.exit()
})
Example: Real-time Server Dashboard
import { GenericModrinthClient } from '@modrinth/api-client'
const client = new GenericModrinthClient({ token: 'auth-token' })
const serverId = 'server-id'
// Connect to server
await client.archon.sockets.safeConnect(serverId)
// Build real-time dashboard
const dashboard = {
powerState: 'unknown',
cpuUsage: 0,
ramUsage: 0,
ramTotal: 0,
uptime: 0,
recentLogs: [],
activeBackups: new Map()
}
// Update power state
client.archon.sockets.on(serverId, 'power-state', (event) => {
dashboard.powerState = event.state
updateUI()
})
// Update stats every few seconds
client.archon.sockets.on(serverId, 'stats', (event) => {
dashboard.cpuUsage = event.cpu_percent
dashboard.ramUsage = event.ram_usage_bytes
dashboard.ramTotal = event.ram_total_bytes
updateUI()
})
// Update uptime
client.archon.sockets.on(serverId, 'uptime', (event) => {
dashboard.uptime = event.uptime
updateUI()
})
// Track recent logs
client.archon.sockets.on(serverId, 'log', (event) => {
dashboard.recentLogs.push(event)
if (dashboard.recentLogs.length > 100) {
dashboard.recentLogs.shift() // Keep only last 100 logs
}
updateUI()
})
// Track backup progress
client.archon.sockets.on(serverId, 'backup-progress', (event) => {
dashboard.activeBackups.set(event.id, {
task: event.task,
progress: event.progress,
state: event.state
})
if (event.state === 'done' || event.state === 'failed') {
setTimeout(() => {
dashboard.activeBackups.delete(event.id)
updateUI()
}, 3000) // Remove after 3 seconds
}
updateUI()
})
function updateUI() {
console.clear()
console.log('=== Server Dashboard ===')
console.log(`Power State: ${dashboard.powerState}`)
console.log(`CPU: ${dashboard.cpuUsage.toFixed(1)}%`)
console.log(`RAM: ${(dashboard.ramUsage / 1024**3).toFixed(2)}GB / ${(dashboard.ramTotal / 1024**3).toFixed(2)}GB`)
console.log(`Uptime: ${Math.floor(dashboard.uptime / 3600)}h ${Math.floor((dashboard.uptime % 3600) / 60)}m`)
if (dashboard.activeBackups.size > 0) {
console.log('\nActive Backups:')
dashboard.activeBackups.forEach((backup, id) => {
const percent = (backup.progress * 100).toFixed(1)
console.log(` ${backup.task}: ${percent}% (${backup.state})`)
})
}
console.log('\nRecent Logs:')
dashboard.recentLogs.slice(-10).forEach((log) => {
console.log(` [${log.stream}] ${log.message}`)
})
}
WebSocket functionality is only available in the GenericModrinthClient, which uses the browser’s native WebSocket API. It is not available in:
NuxtModrinthClient (SSR context)
TauriModrinthClient (use Tauri’s WebSocket plugin instead)
import { GenericModrinthClient } from '@modrinth/api-client'
// WebSocket support available
const client = new GenericModrinthClient({ token: 'auth-token' })
client.archon.sockets // WebSocketClient instance