# Game node operations

Game nodes run Docker workloads for Minecraft servers and connect to the control plane through the node-agent.

## Capacity

Each node exposes:

- `maxServers`, `maxRamMb`, `platformRamMb`
- Host port pool: `portRangeStart`–`portRangeEnd` (default `25565`–`25664`)

The scheduler places new servers on the least-loaded eligible node.

## Enrollment

1. Create a `GameNode` row in the control plane database.
2. Generate an enrollment token and distribute it securely to the node host.
3. Start `node-agent` with matching `NODE_ID` and `NODE_TOKEN`.
4. Confirm node status becomes `ONLINE` in the admin API.

## Port allocation

Host ports are unique per node (`@@unique([nodeId, hostPort])`). Do not manually assign ports outside the configured range.

## Draining

Before maintenance:

1. Set node status to `DRAINING` via admin API.
2. Wait for running servers to stop or migrate.
3. Perform maintenance.
4. Return node to `ONLINE` or decommission.

## Firewall

Allow:

- Outbound WebSocket to control plane API
- Inbound TCP on allocated Minecraft host port range
- Docker bridge traffic locally

Deny:

- Public access to Docker API
- Unrestricted inbound on management ports

## Ansible

Bootstrap with:

```bash
ansible-playbook -i inventories/production deploy/ansible/game-node.yml
```

Copy the `hgc-node-agent` binary to `/usr/local/bin/hgc-node-agent` and provide TLS materials via `NODE_TLS_CERT` / `NODE_TLS_KEY` or CA enrollment.

## Failure scenarios

| Scenario | Action |
|----------|--------|
| Node offline | Servers marked `UNKNOWN`; reschedule starts when node returns |
| Port exhaustion | Provisioning fails with `NO_NODE_AVAILABLE`; add node or expand port range |
| Disk full | Stop new provisions; prune unused images; expand volume |
