11 KiB
HomesteadGateway
Gateway between multiple HomesteadRelay's and the HomesteadToGo Bot.
HomesteadGateway Developer Documentation
Overview
HomesteadGateway is a WebSocket-based message routing gateway that facilitates bidirectional communication between game server mods/plugins and external bots (e.g., Discord bots). It acts as a relay, routing messages based on channel identifiers and managing message queues when endpoints are offline.
Key Features:
- WebSocket-based real-time communication
- Channel-based message routing
- Automatic message queuing for offline recipients
- API key authentication
- Connection keep-alive via ping/pong
- Support for multiple concurrent mod connections per channel
Architecture
Connection Types
The gateway supports two types of connections:
- Mod Connection - Game server mods/plugins that send and receive player messages
- Bot Connection - External bots (typically Discord) that bridge messages to/from other platforms
Message Flow
Mod (Server) ←→ Gateway ←→ Bot (Discord)
↓ ↓
Channel A Channel A
Messages are routed based on channel_id:
- Mod → Bot: Messages from a mod are forwarded to the bot
- Bot → Mod: Messages from a bot are forwarded to the mod registered for that channel
Connection Setup
Endpoint
ws://<host>:<port>/sync?api_key=<your_api_key>
Default Port: 3333 (configurable)
Authentication
Authentication is performed via API key, which can be provided in two ways:
-
Query Parameter (recommended):
ws://localhost:3333/sync?api_key=gateway -
HTTP Header:
X-API-Key: gateway
Connection Timeout
After connecting, you must send a handshake message within 60 seconds or the connection will be closed.
Handshake Protocol
Step 1: Establish WebSocket Connection
Connect to the /sync endpoint with your API key.
Step 2: Send Handshake Message
Immediately after connecting, send a JSON handshake message:
{
"type": "mod", // or "bot"
"data": { ... }
}
Mod Handshake
For game server mods/plugins:
{
"type": "mod",
"data": {
"server_id": "minecraft-server-001",
"channel_id": "123456789"
}
}
Fields:
server_id(string, required): Unique identifier for your server instancechannel_id(string, required): The Discord channel ID (or equivalent) this mod serves
Bot Handshake
For bots (Discord bots, etc.):
{
"type": "bot",
"data": {
"channel_id": "123456789"
}
}
Fields:
channel_id(string, required): The channel ID this bot monitors
Note: Only one bot connection is allowed at a time. Connecting a new bot will close the existing bot connection.
Step 3: Receive Acknowledgment
After sending the handshake, wait for an acknowledgment:
Success Response:
{
"status": "connected",
"type": "mod" // or "bot"
}
Error Responses:
{
"message": "Malformed handshake.",
"code": 400
}
{
"message": "Bot already connected.",
"code": 409
}
Step 4: Begin Message Exchange
Once acknowledged, the connection is established and you can start sending/receiving messages.
Message Protocol
Sending Messages
After handshake, send messages as JSON:
From Mod to Bot
{
"msg_id": "msg-unique-123",
"id": "minecraft-server-001",
"destination": {
"channel_id": "123456789"
},
"author": {
"id": "player-uuid-abc",
"name": "PlayerName"
},
"content": "Hello from the game server!",
"meta": {
"server_name": "Survival Server",
"world": "overworld"
},
"ts": "2025-12-08T10:30:00Z"
}
Required Fields:
msg_id(string): Unique message identifier (generate client-side)id(string): Server ID (must match your handshakeserver_id)destination.channel_id(string): Target channel IDauthor.id(string): User/player identifiercontent(string): Message content (non-empty)
Optional Fields:
author.name(string): Display name for the authormeta(object): Additional metadata (arbitrary key-value pairs)ts(RFC3339 timestamp): Message timestamp (defaults to server time if omitted)
From Bot to Mod
{
"msg_id": "discord-msg-456",
"id": "123456789",
"author": {
"id": "discord-user-789",
"name": "DiscordUser"
},
"content": "Hello from Discord!",
"meta": {
"platform": "discord",
"roles": ["admin"]
},
"ts": "2025-12-08T10:31:00Z"
}
Required Fields:
msg_id(string): Unique message identifierid(string): Channel ID (from which channel the message originates)author.id(string): User identifiercontent(string): Message content (non-empty)
Optional Fields:
author.name(string): Display namemeta(object): Additional metadatats(RFC3339 timestamp): Message timestamp
Note: Bot messages do not include a destination field, as the channel ID in id determines routing.
Receiving Messages
Messages are received as JSON in the same format they were sent:
Mod Receives (from Bot)
{
"type": "bot",
"channel_id": "123456789",
"author": {
"id": "discord-user-789",
"name": "DiscordUser"
},
"content": "Hello from Discord!",
"meta": {
"platform": "discord"
},
"ts": "2025-12-08T10:31:00Z",
"received_at": "2025-12-08T10:31:00.123Z",
"forwarded_at": "2025-12-08T10:31:00.125Z"
}
Bot Receives (from Mod)
{
"type": "mod",
"channel_id": "123456789",
"author": {
"id": "player-uuid-abc",
"name": "PlayerName"
},
"content": "Hello from the game server!",
"meta": {
"server_name": "Survival Server"
},
"ts": "2025-12-08T10:30:00Z",
"received_at": "2025-12-08T10:30:00.100Z",
"forwarded_at": "2025-12-08T10:30:00.102Z"
}
Additional Fields in Received Messages:
type(string): Origin type ("mod" or "bot")channel_id(string): The channel this message belongs toreceived_at(RFC3339): When gateway received the messageforwarded_at(RFC3339): When gateway forwarded the message
Message Acknowledgments
After sending each message, you'll receive an acknowledgment:
{
"status": "completed",
"type": "mod"
}
Status Values:
completed: Message was delivered immediately to recipientqueued: Recipient is offline; message queued for later deliveryfailed: Message could not be delivered or queued
Message Queuing
When a recipient is offline, messages are automatically queued:
- Queue Size: Configurable (default: 8 messages per channel)
- Queue Behavior: Circular buffer (oldest messages are overwritten when full)
- Flush Trigger: When recipient reconnects, all queued messages are delivered
Example Flow
- Mod sends message while bot is offline → Message queued
- Bot connects and completes handshake → All queued messages flushed to bot
- Bot sends message while mod is offline → Message queued
- Mod connects → Queued messages flushed to mod
Keep-Alive & Ping/Pong
The gateway sends WebSocket ping messages every 30 seconds to maintain connections.
Client Responsibilities:
- Respond to pings: Your WebSocket library should automatically handle pong responses
- Handle pongs: Set a pong handler to reset read deadlines
- Read Deadline: The gateway sets a 60-second read deadline, reset on each pong
Example (Go)
conn.SetPongHandler(func(appData string) error {
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
Error Handling
Connection Errors
Handshake Errors:
400- Malformed handshake JSON401- Invalid or missing API key409- Bot already connected (for bot handshakes)500- Internal server error
Message Errors:
400- Malformed message (missing required fields)
Errors are sent as JSON:
{
"message": "Malformed message.",
"code": 400
}
Validation Rules
Messages are validated on receipt:
idmust not be emptymsg_idmust not be emptyauthor.idmust not be emptycontentmust not be empty- For mod messages:
destination.channel_idmust not be empty
Websocket Closures
1000- Normal closure1001- Going away
Handle other WebSocket closures as unexpected errors.
Rate Limits & Restrictions
- Message Size Limit: 1 MB per message (configurable)
- Read Limit: Messages exceeding the limit will close the connection
- Concurrent Mods: Multiple mods can connect to the same channel ID (different server IDs)
- Concurrent Bots: Only one bot connection allowed globally
Best Practices
1. Generate Unique Message IDs
Always generate unique msg_id values for each message. Consider using:
- UUID v4
- Timestamp + random suffix
- Sequential counter with prefix
2. Handle Reconnections
Implement automatic reconnection logic with exponential backoff:
1st retry: 1 second
2nd retry: 2 seconds
3rd retry: 4 seconds
Max: 30 seconds
3. Set Appropriate Timeouts
- Write timeout: 5 seconds (same as Gateway)
- Read timeout: 60 seconds (reset on pong)
4. Validate Before Sending
Check required fields locally before sending to avoid validation errors.
5. Monitor Acknowledgments
Track acknowledgment statuses:
completed: Message deliveredqueued: Message queuedfailed: Log and potentially retry
6. Use Metadata
The meta field is used for:
- Server information (server name, region, version)
- User context (roles, permissions)
- Message context (reply-to, thread-id)
7. Thread-Safe Writes
Use mutex/locks when writing to WebSocket from multiple threads.
Troubleshooting
Connection Refused
- Verify the gateway is running
- Check the port is correct (default: 3333)
- Ensure firewall allows connections
401 Unauthorized
- Verify API key is correct
- Check API key is properly URL-encoded in query string
Handshake Timeout
- Ensure handshake is sent within 60 seconds of connection
- Verify handshake JSON is correctly formatted
Messages Not Received
- Check
channel_idmatches between mod and bot - Verify recipient is connected
- Check queue status in acknowledgments
Connection Drops
- Ensure pong responses are sent to pings
- Check network stability
- Implement reconnection logic
API Reference
Endpoints
GET /sync
WebSocket upgrade endpoint for mod/bot connections.
Query Parameters:
api_key(required): Authentication token
GET /health
Health check endpoint.
Response:
{
"status": "healthy"
}
Configuration
Gateway configuration (config.toml):
[gateway]
http_port = 3333 # WebSocket port
websocket = "gateway" # API key
body_size = 1 # Max message size in MB
queue_max = 8 # Messages per channel queue