Files
HomesteadGateway/README.md
2025-12-08 09:36:40 +01:00

478 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
---
## Quick Start
1. Start the gateway (build/run your application that embeds the WebSocket gateway).
2. Connect a client to the WebSocket endpoint:
- URL: `ws://<host>:<port>/sync?api_key=<your_api_key>`
3. Immediately send a handshake JSON with type `mod` or `bot`.
4. On success, youll receive `{"status":"connected","type":"mod|bot"}`.
5. Exchange messages as JSON.
See the full details below.
---
## Architecture
### Connection Types
The gateway supports two types of connections:
1. **Mod Connection** - Game server mods/plugins that send and receive player messages
2. **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:
1. **Query Parameter** (recommended):
```
ws://localhost:3333/sync?api_key=gateway
```
2. **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:
```json
{
"type": "mod", // or "bot"
"data": { ... }
}
```
#### Mod Handshake
For game server mods/plugins:
```json
{
"type": "mod",
"data": {
"server_id": "minecraft-server-001",
"channel_id": "123456789"
}
}
```
**Fields:**
- `server_id` (string, required): Unique identifier for your server instance
- `channel_id` (string, required): The Discord channel ID (or equivalent) this mod serves
#### Bot Handshake
For bots (Discord bots, etc.):
```json
{
"type": "bot",
"data": {
"bot_id": "discord-bot-123"
}
}
```
**Fields:**
- `bot_id` (string, required): The bots ID
**Note:** Only **one bot connection** is allowed at a time. Trying to connect a new bot will result in an `409 Conflict` Error.
### Step 3: Receive Acknowledgment
After sending the handshake, wait for an acknowledgment:
**Success Response:**
```json
{
"status": "connected",
"type": "mod|bot"
}
```
**Error Responses:**
```json
{
"message": "Malformed handshake.",
"code": 400
}
```
```json
{
"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
```json
{
"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 handshake `server_id`)
- `destination.channel_id` (string): Target channel ID
- `author.id` (string): User/player identifier
- `content` (string): Message content (non-empty)
**Optional Fields:**
- `author.name` (string): Display name for the author
- `meta` (object): Additional metadata (arbitrary key-value pairs)
- `ts` (RFC3339 timestamp): Message timestamp (defaults to server time if omitted)
#### From Bot to Mod
```json
{
"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 identifier
- `id` (string): Channel ID (from which channel the message originates)
- `author.id` (string): User identifier
- `content` (string): Message content (non-empty)
**Optional Fields:**
- `author.name` (string): Display name
- `meta` (object): Additional metadata
- `ts` (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)
```json
{
"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)
```json
{
"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 to
- `received_at` (RFC3339): When gateway received the message
- `forwarded_at` (RFC3339): When gateway forwarded the message
### Message Acknowledgments
After sending each message, you'll receive an acknowledgment:
```json
{
"status": "completed",
"type": "mod"
}
```
**Status Values:**
- `completed`: Message was delivered immediately to recipient
- `queued`: Recipient is offline; message queued for later delivery
- `failed`: 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
1. Mod sends message while bot is offline → Message queued
2. Bot connects and completes handshake → All queued messages flushed to bot
3. Bot sends message while mod is offline → Message queued
4. 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:**
1. **Respond to pings**: Your WebSocket library should automatically handle pong responses
2. **Handle pongs**: Set a pong handler to reset read deadlines
3. **Read Deadline**: The gateway sets a 60-second read deadline, reset on each pong
### Example (Go)
```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 JSON
- `401` - Invalid or missing API key
- `409` - Bot already connected (only for bot handshakes)
- `500` - Internal server error
**Message Errors:**
- `400` - Malformed message (missing required fields)
Errors are sent as JSON:
```json
{
"message": "Malformed message.",
"code": 400
}
```
### Validation Rules
Messages are validated on receipt:
- `id` must not be empty
- `msg_id` must not be empty
- `author.id` must not be empty
- `content` must not be empty
- For mod messages: `destination.channel_id` must not be empty
### Websocket Closures
- `1000` - Normal closure
- `1001` - Going away
Handle other WebSocket closures as unexpected errors.
---
## Rate Limits & Restrictions
- **Message Size Limit:** 2 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 delivered
- `queued`: Message queued
- `failed`: 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.
---
## 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:**
```json
{
"status": "healthy"
}
```
---
## Configuration
Gateway configuration (`config.toml`):
```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
```
---