synckit

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 14 Imported by: 0

README

synckit

Shared deploy and update spine for sync servers. Gives you a Discord bot (/verify, /updateserver), a declarative deploy-agent, Discord OAuth, generic Postgres plumbing, and a stable HTTP contract. Consumed by EggLedger and EggIncognito sync servers.

Consume it (Go)

import (
	"log"
	"os"

	synckit "github.com/DavidArthurCole/synckit"
	"github.com/DavidArthurCole/synckit/contract"
)

func main() {
	err := synckit.Run(synckit.AppProfile{
		Name: "EggLedger",
		RepoURL: "https://github.com/DavidArthurCole/EggLedgerSyncServer",
		Discord: synckit.DiscordConfig{
			Token: os.Getenv("DISCORD_BOT_TOKEN"),
			AppID: os.Getenv("DISCORD_CLIENT_ID"),
			GuildID: os.Getenv("DISCORD_GUILD_ID"),
		},
		Build: contract.VerifyInfo{SHA256: BuildSHA256, Version: BuildVersion, Date: BuildDate},
		DeployAgent: synckit.AgentRef{
			URL: os.Getenv("DEPLOY_AGENT_URL"),
			Secret: os.Getenv("DEPLOY_AGENT_SECRET"),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
}

Build-info vars BuildSHA256, BuildVersion, BuildDate are injected by the consumer via -ldflags "-X main.BuildVersion=...".

AppProfile fields: Name, RepoURL, Discord, Build, DeployAgent, DB (nil runs DB-free), Commands, Events, Mux. A nil Mux makes synckit create one.

Shared role self-assign

DiscordConfig.SharedRoleID (and bot.Config.SharedRoleID) is an optional snowflake ID of a manually-created guild role. When set alongside GuildID, the bot grants this role to its own member on the gateway Ready event. Best-effort: failures are logged and ignored.

Requirements: the bot needs the Manage Roles permission, and the shared role must sit below the bot's own highest role in the guild's role hierarchy, or Discord rejects the assignment.

Deploy-agent

A host-side binary, needs docker and git. Runs a declarative pipeline on POST. Define deploy-agent.yaml:

repo: /home/david/repos/EggLedgerSyncServer
steps:
  - git-pull
  - docker-build: { tag: ledgersyncserver:latest }
  - container-recreate: { name: ledgersync }
  - webhook: { url_env: PORTAINER_WEBHOOK_URL }

Step types: git-pull, docker-build, container-recreate, webhook, shell. git-pull records from and to short hashes and short-circuits on "Already up to date." shell runs arbitrary commands, so yaml is trusted operator input. A consumer parses the file with agent.ParseConfig, builds an agent.Executor, and serves it via agent.NewHandler(secret, exec.Run).

Frame host setup

The sync server runs in a container. The deploy-agent runs on the host under systemd because it needs the docker socket and git. To register it:

cd deploy
cp install.sh.tmpl install.sh   # edit the vars at the top
bash install.sh

This writes /etc/synckit/<app>.env, installs synckit-agent-<app>.service, runs daemon-reload, and enables and starts it. The bot's /updateserver then POSTs to the agent, which pulls, builds, recreates, and pings the Portainer webhook.

HTTP contract

The contract package holds frozen JSON types shared with non-Go consumers like the .NET app and the device farm: DeployResponse, VerifyInfo, NewVersionEvent. The device farm POSTs NewVersionEvent to /events/new-version, bearer-authed via EventHandlers.EventSecret, when it detects a new game version. The consumer's Events.NewVersion handler then drives generate-and-deploy.

Packages

Package Purpose
contract Frozen HTTP JSON types.
bot Discord bot: verify, updateserver, command dispatch, embeds.
agent Deploy-agent: typed-step pipeline, yaml config, HTTP handler.
auth Discord OAuth helpers and bearer-token middleware.
db Generic Postgres init and migration runner, no app schema.
synckit (root) AppProfile, Run, NewVersionHandler.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewVersionHandler

func NewVersionHandler(secret string, fn func(contract.NewVersionEvent) error) http.Handler

NewVersionHandler is the bearer-authed inbound endpoint the device farm POSTs to when it detects a new game version.

func Run

func Run(p AppProfile) error

Run stands up the sync server from a profile: optional DB, bot, event routes. It blocks until SIGINT/SIGTERM, then cleans up.

Types

type AgentRef

type AgentRef struct {
	URL    string
	Secret string
}

AgentRef points the bot at a deploy-agent.

type AppProfile

type AppProfile struct {
	Name        string
	Discord     DiscordConfig
	Build       contract.VerifyInfo
	DeployAgent AgentRef
	DB          *DBConfig
	Commands    []bot.Command
	Events      EventHandlers
	Mux         *http.ServeMux
	RepoURL     string
}

AppProfile is the single struct a Go consumer fills to stand up a sync server.

type DBConfig

type DBConfig struct {
	ConnStr       string
	MigrationsDir string
}

DBConfig configures optional Postgres. Nil profile.DB = run DB-free.

type DiscordConfig

type DiscordConfig struct {
	Token             string
	AppID             string
	GuildID           string
	OAuthClientID     string
	OAuthClientSecret string
	OAuthRedirectURL  string
	SharedRoleID      string
}

DiscordConfig carries gateway + OAuth identifiers.

type EventHandlers

type EventHandlers struct {
	NewVersion  func(contract.NewVersionEvent) error
	EventSecret string
}

EventHandlers carries optional inbound HTTP event hooks.

Directories

Path Synopsis
Package contract holds the stable JSON types shared across the synckit HTTP surface, between the bot, the deploy-agent, and any non-Go consumer such as the .NET app or the device farm.
Package contract holds the stable JSON types shared across the synckit HTTP surface, between the bot, the deploy-agent, and any non-Go consumer such as the .NET app or the device farm.
Package db provides generic Postgres plumbing, connection plus migration running, with no app-specific schema.
Package db provides generic Postgres plumbing, connection plus migration running, with no app-specific schema.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL