Authentication¶
Authentication is disabled by default. Enable it to secure your server and enforce per-app rate limits.
Enabling Authentication¶
Set require_websocket_auth to true and add authorized apps:
{
"security": {
"require_websocket_auth": true,
"authorized_apps": [
{
"app_id": "my-game",
"app_secret": "your-secret-here",
"app_name": "My Game",
"max_rooms": 100,
"max_players_per_room": 16,
"rate_limit_per_minute": 60
}
]
}
}
Important: Change the default app_secret before deploying to production. The example value
CHANGE_ME_BEFORE_PRODUCTION in config.example.json is intentionally insecure.
Client Authentication¶
When auth is enabled, clients must send an Authenticate message immediately after connecting:
const ws = new WebSocket('ws://localhost:3536/v2/ws');
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'Authenticate',
data: {
app_id: 'my-game'
}
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'Authenticated') {
console.log('Authenticated successfully');
// Now you can create/join rooms
}
if (message.type === 'Error' && message.data.error_code === 'AUTHENTICATION_REQUIRED') {
console.error('Authentication failed');
}
};
Per-App Settings¶
Each authorized app has its own limits:
{
"app_id": "my-game",
"app_secret": "secret-key",
"app_name": "My Game",
"max_rooms": 100,
"max_players_per_room": 16,
"rate_limit_per_minute": 60
}
app_id- Unique identifier for the appapp_secret- Secret key for authenticationapp_name- Human-readable name (for logging/metrics)max_rooms- Maximum concurrent rooms for this appmax_players_per_room- Max players per room for this apprate_limit_per_minute- Max requests per minute per IP for this app
Auth Timeout¶
Clients must authenticate within the configured timeout:
If the client doesn't send Authenticate within this window, the connection is closed.
Metrics Authentication¶
Protect the /metrics endpoints:
When enabled, metrics endpoints require an Authorization header:
Format: Bearer <app_id>:<app_secret>
Error Codes¶
Common auth-related errors:
AUTHENTICATION_REQUIRED- Authentication is required but not providedINVALID_APP_ID- Invalid app IDAUTHENTICATION_TIMEOUT- Client did not authenticate in timeMAX_ROOMS_PER_GAME_EXCEEDED- App has reached its max rooms limit
Example: Multiple Apps¶
{
"security": {
"require_websocket_auth": true,
"authorized_apps": [
{
"app_id": "production-game",
"app_secret": "prod-secret-key",
"app_name": "Production Game",
"max_rooms": 1000,
"max_players_per_room": 16,
"rate_limit_per_minute": 100
},
{
"app_id": "dev-game",
"app_secret": "dev-secret-key",
"app_name": "Development Game",
"max_rooms": 10,
"max_players_per_room": 4,
"rate_limit_per_minute": 20
}
]
}
}
Security Best Practices¶
- Never commit secrets - Use environment variables in production
- Generate strong secrets - Use a password generator or
openssl rand -base64 32 - Rotate secrets regularly - Update secrets periodically
- Use HTTPS in production - Protect credentials in transit
- Monitor failed auth attempts - Watch for brute-force attacks
Environment Variables¶
Override app secrets via environment:
# Not recommended - shown for reference only
SIGNAL_FISH_SECURITY__AUTHORIZED_APPS='[{"app_id":"my-game","app_secret":"env-secret",...}]'
Better approaches for production secrets management:
Docker Secrets (Docker Swarm / Compose)¶
# docker-compose.yml
version: '3.8'
services:
signal-fish:
image: ghcr.io/ambiguous-interactive/signal-fish-server:latest
secrets:
- signal_fish_config
entrypoint: sh -c "cp /run/secrets/signal_fish_config /app/config.json && /app/signal-fish-server"
secrets:
signal_fish_config:
file: ./config.secret.json
Kubernetes ConfigMap and Secrets¶
# Create secret from file
kubectl create secret generic signal-fish-config --from-file=config.json=./config.secret.json
# deployment.yaml
apiVersion: v1
kind: Deployment
metadata:
name: signal-fish-server
spec:
template:
spec:
containers:
- name: signal-fish
image: ghcr.io/ambiguous-interactive/signal-fish-server:latest
volumeMounts:
- name: config
mountPath: /app/config.json
subPath: config.json
readOnly: true
volumes:
- name: config
secret:
secretName: signal-fish-config
Environment Variable Templating¶
Generate config.json at runtime from environment variables:
#!/bin/bash
# entrypoint.sh
cat > /app/config.json <<EOF
{
"port": ${PORT:-3536},
"security": {
"require_websocket_auth": true,
"authorized_apps": [
{
"app_id": "${APP_ID}",
"app_secret": "${APP_SECRET}",
"app_name": "${APP_NAME}",
"max_rooms": ${MAX_ROOMS:-100},
"max_players_per_room": ${MAX_PLAYERS:-16},
"rate_limit_per_minute": ${RATE_LIMIT:-60}
}
]
}
}
EOF
exec /app/signal-fish-server
AWS Secrets Manager¶
# Fetch secret and write to config file
aws secretsmanager get-secret-value \
--secret-id signal-fish-config \
--query SecretString \
--output text > /app/config.json
# Start server
/app/signal-fish-server
HashiCorp Vault¶
# Fetch secret from Vault
vault kv get -field=config secret/signal-fish > /app/config.json
# Start server
/app/signal-fish-server
Next Steps¶
- Configuration - Full configuration reference
- Deployment - Production deployment guide