feat: Add Go companion agent for bare metal server management

Implements complete companion agent for Rust servers not on managed panels.

Features:
- NATS integration with token auth and auto-reconnect
- Game server process management (start/stop/restart/monitor)
- File operations (read/write/delete/list) via NATS
- SteamCMD integration for automated updates
- Self-update capability with download and replace
- Heartbeat publishing every 60s with server status
- Graceful shutdown handling (SIGTERM/SIGINT)
- Zombie process prevention via cmd.Wait()
- Cross-platform builds (Linux amd64, Windows amd64)

Structure:
- cmd/agent/main.go: Entry point, config, signal handling
- internal/app/daemon.go: Main loop, NATS subscriptions
- internal/client/nats.go: NATS connection with reconnect
- internal/process/gameserver.go: Process management
- internal/process/steamcmd.go: Steam update execution
- internal/files/operations.go: File system operations
- internal/update/updater.go: Self-update logic
- Makefile: Cross-compilation targets
- README.md: Installation and configuration guide

NATS Subjects:
- Publishes: corrosion.{license_id}.companion.heartbeat
- Publishes: corrosion.{license_id}.files.response
- Subscribes: corrosion.{license_id}.cmd.server
- Subscribes: corrosion.{license_id}.files.{get|put|delete|list}
- Subscribes: corrosion.{license_id}.update.steam
- Subscribes: corrosion.{license_id}.update.companion

Built binaries: 7.0MB (Linux), 7.2MB (Windows)
Total code: 1,356 LOC across 8 files

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-15 12:05:23 -05:00
parent 8bea889145
commit a62715409f
13 changed files with 1735 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
package client
import (
"fmt"
"time"
"github.com/nats-io/nats.go"
)
// Connect establishes a connection to NATS with token authentication
// and automatic reconnection handling
func Connect(url, token string) (*nats.Conn, error) {
opts := []nats.Option{
nats.Token(token),
nats.Name("corrosion-companion"),
nats.MaxReconnects(-1), // Unlimited reconnect attempts
nats.ReconnectWait(2 * time.Second),
nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
if err != nil {
fmt.Printf("NATS disconnected: %v\n", err)
}
}),
nats.ReconnectHandler(func(nc *nats.Conn) {
fmt.Printf("NATS reconnected to %s\n", nc.ConnectedUrl())
}),
nats.ClosedHandler(func(nc *nats.Conn) {
fmt.Println("NATS connection closed")
}),
nats.ErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) {
fmt.Printf("NATS error: %v\n", err)
}),
}
nc, err := nats.Connect(url, opts...)
if err != nil {
return nil, fmt.Errorf("failed to connect to NATS: %w", err)
}
return nc, nil
}