agentlink

package
v0.1.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package agentlink is the panel side of the agent connection (docs/agent-protocol.md): a WebSocket over mTLS on a dedicated listener, carrying JSON messages.

Index

Constants

View Source
const (
	TypeHello   = "hello"    // agent -> panel, first message
	TypeHelloOK = "hello.ok" // panel -> agent
	TypeError   = "error"    // either direction, connection-level error
)

Message types.

View Source
const (
	TypeOK               = "ok"                // reply: request accepted
	TypeServiceCreate    = "service.create"    // panel -> agent: ServiceSpec; agent installs, then reports state
	TypeServiceReinstall = "service.reinstall" // panel -> agent: ServiceSpec; wipe nothing, rerun install
	TypeServiceDelete    = "service.delete"    // panel -> agent: ServiceRef; stop, remove container and data
	TypeServicePower     = "service.power"     // panel -> agent: PowerRequest
	TypeServiceState     = "service.state"     // agent -> panel: ServiceState (event, no reply)
)

Service message types (docs/agent-protocol.md).

View Source
const (
	StatePending       = "pending" // panel only: not delivered to the agent yet
	StateInstalling    = "installing"
	StateInstallFailed = "install_failed"
	StateOffline       = "offline"
	StateStarting      = "starting"
	StateRunning       = "running"
	StateStopping      = "stopping"
	StateCrashed       = "crashed"
)

Service states reported by the agent.

View Source
const (
	TypeConsoleBacklog     = "console.backlog"     // panel -> agent: ServiceRef; reply ConsoleLines
	TypeConsoleSubscribe   = "console.subscribe"   // panel -> agent: ServiceRef; stream lines and stats
	TypeConsoleUnsubscribe = "console.unsubscribe" // panel -> agent: ServiceRef
	TypeConsoleCommand     = "console.command"     // panel -> agent: ConsoleCommand
	TypeConsoleLine        = "console.line"        // agent -> panel event: ConsoleLines
	TypeStats              = "stats"               // agent -> panel event: Stats
)

Console and statistics message types.

View Source
const (
	TypeFilesList   = "files.list"   // FilePath -> FileList
	TypeFilesRead   = "files.read"   // FileRead -> FileChunk
	TypeFilesWrite  = "files.write"  // FileWrite
	TypeFilesMkdir  = "files.mkdir"  // FilePath
	TypeFilesRename = "files.rename" // FileRename
	TypeFilesDelete = "files.delete" // FileDelete
)

File operations (panel -> agent); paths are relative to the service's data directory.

View Source
const (
	TypeWebsiteApply  = "website.apply"  // panel -> agent: WebsiteSpec (create or update, idempotent)
	TypeWebsiteDelete = "website.delete" // panel -> agent: ServiceRef; removes the account, its files and databases
	TypeWebsiteStatus = "website.status" // panel -> agent: ServiceRef -> WebsiteStatus
	TypeWebsiteDB     = "website.db"     // panel -> agent: WebsiteDB -> WebsiteDBList
	TypeWebsiteBackup = "website.backup" // panel -> agent: WebsiteBackup -> WebsiteBackupList
	TypeWebsiteApp    = "website.app"    // panel -> agent: WebsiteApp (WordPress install, Git deploy)
)

Web hosting (Phase 3). A website is one hosting account: a Linux user, its own PHP-FPM master (systemd unit with cgroup limits), Caddy site blocks, MariaDB databases and cron.

View Source
const ChunkSize = 512 << 10

ChunkSize is the largest piece of file data in one message.

View Source
const ConnectPath = "/api/agent/v1/connect"

ConnectPath is where agents open their WebSocket on the agent listener.

View Source
const Protocol = 1

Protocol is the protocol version this panel speaks.

View Source
const TypeCertRenew = "cert.renew"

TypeCertRenew is an agent -> panel request: CertRenew, answered with CertRenewed.

View Source
const TypeSFTPAuth = "sftp.auth"

TypeSFTPAuth is an agent -> panel request: SFTPAuth, answered with SFTPLogin.

Variables

View Source
var ErrOffline = errors.New("node is offline")

ErrOffline is returned by Request when the node has no live connection.

Functions

This section is empty.

Types

type AllocationSpec

type AllocationSpec struct {
	IP   string `json:"ip"`
	Port int    `json:"port"`
}

AllocationSpec is the IP:port the service listens on (TCP and UDP).

type CertRenew

type CertRenew struct {
	CSR string `json:"csr"`
}

CertRenew carries a certificate request for a fresh node key.

type CertRenewed

type CertRenewed struct {
	Certificate string    `json:"certificate"`
	ExpiresAt   time.Time `json:"expires_at"`
}

CertRenewed is the new client certificate (PEM).

type ConfigFileSpec

type ConfigFileSpec struct {
	Path   string          `json:"path"`
	Parser string          `json:"parser"`
	Find   json.RawMessage `json:"find"`
}

ConfigFileSpec is one egg config file rewrite (applied by the agent before each start).

type ConsoleCommand

type ConsoleCommand struct {
	ID      string `json:"id"`
	Command string `json:"command"`
}

ConsoleCommand is a line typed into the server console.

type ConsoleLines

type ConsoleLines struct {
	ID    string   `json:"id"`
	Lines []string `json:"lines"`
}

ConsoleLines carries console output (newest last).

type CronJob

type CronJob struct {
	Schedule string `json:"schedule"`
	Command  string `json:"command"`
}

CronJob runs a command as the account user.

type FileChunk

type FileChunk struct {
	Size int64  `json:"size"`
	Data []byte `json:"data"`
	EOF  bool   `json:"eof"`
}

FileChunk is file data.

type FileDelete

type FileDelete struct {
	ID    string   `json:"id"`
	Paths []string `json:"paths"`
}

FileDelete removes files and directories.

type FileEntry

type FileEntry struct {
	Name    string `json:"name"`
	Size    int64  `json:"size"`
	Mode    string `json:"mode"`
	ModTime int64  `json:"mod_time"`
	IsDir   bool   `json:"is_dir"`
	Symlink bool   `json:"symlink"`
}

FileEntry is one directory entry.

type FileList

type FileList struct {
	Path    string      `json:"path"`
	Entries []FileEntry `json:"entries"`
}

FileList is the content of a directory.

type FilePath

type FilePath struct {
	ID   string `json:"id"`
	Path string `json:"path"`
}

FilePath names a file or directory of a service.

type FileRead

type FileRead struct {
	ID     string `json:"id"`
	Path   string `json:"path"`
	Offset int64  `json:"offset"`
	Length int    `json:"length"`
}

FileRead asks for up to Length bytes at Offset.

type FileRename

type FileRename struct {
	ID        string `json:"id"`
	From      string `json:"from"`
	To        string `json:"to"`
	Overwrite bool   `json:"overwrite,omitempty"`
}

FileRename moves From to To; Overwrite may replace an existing regular file.

type FileWrite

type FileWrite struct {
	ID      string `json:"id"`
	Path    string `json:"path"`
	Content string `json:"content,omitempty"`
	Data    []byte `json:"data,omitempty"`
	Append  bool   `json:"append,omitempty"`
}

FileWrite writes Content (text) or Data; Append adds to the end.

type Hello

type Hello struct {
	Protocol      int    `json:"protocol"`
	AgentVersion  string `json:"agent_version"`
	Hostname      string `json:"hostname"`
	OS            string `json:"os"`
	Kernel        string `json:"kernel"`
	Arch          string `json:"arch"`
	CPUs          int    `json:"cpus"`
	MemoryBytes   uint64 `json:"memory_bytes"`
	DiskBytes     uint64 `json:"disk_bytes"`
	DiskFreeBytes uint64 `json:"disk_free_bytes"`
	DockerVersion string `json:"docker_version"`
	SFTPPort      int    `json:"sftp_port"`
	// Services the agent knows about and their current state.
	Services []ServiceState `json:"services"`
	// Web is the node's web hosting stack (nil = no web hosting).
	Web *WebCapabilities `json:"web,omitempty"`
	// Websites the agent knows.
	Websites []string `json:"websites,omitempty"`
}

Hello is what the agent reports about itself and its machine when it connects.

type HelloOK

type HelloOK struct {
	Protocol int    `json:"protocol"`
	NodeID   string `json:"node_id"`
}

HelloOK confirms the connection.

type Hub

type Hub struct {

	// OnConnect runs (in its own goroutine) after each successful handshake, e.g. to
	// deliver services created while the node was offline.
	OnConnect func(nodeID string)
	// OnRequest answers requests an agent sends to the panel (e.g. sftp.auth). It runs in its
	// own goroutine; the returned value becomes the reply's data.
	OnRequest func(ctx context.Context, nodeID string, msg Message) (any, error)
	// contains filtered or unexported fields
}

Hub tracks connected agents.

func NewHub

func NewHub(st *store.Store, logger *slog.Logger) *Hub

NewHub returns an empty hub.

func (*Hub) Command

func (h *Hub) Command(ctx context.Context, nodeID, serviceID, command string) error

Command sends a console line to a service.

func (*Hub) Disconnect

func (h *Hub) Disconnect(nodeID string, reason string)

Disconnect closes a node's connection, e.g. after the node was deleted.

func (*Hub) Handler

func (h *Hub) Handler() http.Handler

Handler serves the agent WebSocket. It must run behind the mTLS listener (pki.CA.ServerTLS), which guarantees a client certificate signed by the panel CA.

func (*Hub) Online

func (h *Hub) Online(nodeID string) (bool, time.Time)

Online reports whether a node has a live connection and since when.

func (*Hub) Request

func (h *Hub) Request(ctx context.Context, nodeID, typ string, data any) (Message, error)

Request sends a message to a node's agent and waits for its reply. An agent error reply is returned as an error.

func (*Hub) Unwatch

func (h *Hub) Unwatch(sub *Subscriber)

Unwatch removes a subscriber; the agent stops streaming when the last one leaves.

func (*Hub) Watch

func (h *Hub) Watch(ctx context.Context, nodeID, serviceID string) (*Subscriber, []string, error)

Watch subscribes to a service's console. The agent is asked to stream only while at least one subscriber exists; the returned backlog holds recent output (nil if offline).

type InstallSpec

type InstallSpec struct {
	Container  string `json:"container"`
	Entrypoint string `json:"entrypoint"`
	Script     string `json:"script"`
}

InstallSpec is the egg's install script and the container it runs in.

type Limits

type Limits struct {
	MemoryMB   int `json:"memory_mb"`
	CPUPercent int `json:"cpu_percent"` // 100 = one core; 0 = unlimited
	DiskMB     int `json:"disk_mb"`     // 0 = unlimited
	PIDs       int `json:"pids"`
}

Limits are the container's resource limits.

type Message

type Message struct {
	ID      string          `json:"id,omitempty"`
	Type    string          `json:"type"`
	ReplyTo string          `json:"reply_to,omitempty"`
	Error   string          `json:"error,omitempty"`
	Data    json.RawMessage `json:"data,omitempty"`
}

Message is the envelope for every frame in both directions.

type PowerRequest

type PowerRequest struct {
	ID     string `json:"id"`
	Action string `json:"action"` // start | stop | restart | kill
}

PowerRequest asks the agent to start, stop, restart or kill a service.

type SFTPAuth

type SFTPAuth struct {
	Username string `json:"username"`
	Password string `json:"password"`
	IP       string `json:"ip"`
}

SFTPAuth asks whether a username/password may open a service over SFTP. Usernames are "<email>.<last 8 characters of the service ID>".

type SFTPLogin

type SFTPLogin struct {
	ServiceID string `json:"service_id"`
}

SFTPLogin names the service the user may access.

type ServiceRef

type ServiceRef struct {
	ID string `json:"id"`
}

ServiceRef names a service.

type ServiceSpec

type ServiceSpec struct {
	ID          string            `json:"id"`
	Image       string            `json:"image"`
	Invocation  string            `json:"invocation"` // startup command with variables substituted
	Env         map[string]string `json:"env"`
	Install     InstallSpec       `json:"install"`
	Stop        StopSpec          `json:"stop"`
	DoneMarkers []string          `json:"done_markers"`
	ConfigFiles []ConfigFileSpec  `json:"config_files"`
	Limits      Limits            `json:"limits"`
	Allocation  AllocationSpec    `json:"allocation"`
	// SkipInstall: the files already exist (a server migrated from Wings); a first create
	// adopts them instead of running the install script.
	SkipInstall bool `json:"skip_install,omitempty"`
}

ServiceSpec is a blueprint resolved for one service: everything the agent needs, nothing it must look up.

type ServiceState

type ServiceState struct {
	ID     string `json:"id"`
	State  string `json:"state"`
	Detail string `json:"detail,omitempty"`
}

ServiceState is the agent's report of a service's state.

type Stats

type Stats struct {
	ID               string  `json:"id"`
	CPUPercent       float64 `json:"cpu_percent"`
	MemoryBytes      uint64  `json:"memory_bytes"`
	MemoryLimitBytes uint64  `json:"memory_limit_bytes"`
	NetRxBytes       uint64  `json:"net_rx_bytes"`
	NetTxBytes       uint64  `json:"net_tx_bytes"`
	DiskBytes        uint64  `json:"disk_bytes"`
}

Stats is a resource sample of a running service.

type StopSpec

type StopSpec struct {
	Command string `json:"command,omitempty"`
	Signal  string `json:"signal,omitempty"`
}

StopSpec is how to stop the server: a console command, or a signal.

type Subscriber

type Subscriber struct {
	C chan Message
	// contains filtered or unexported fields
}

Subscriber receives a service's console lines, stats and state changes.

type WebBackup

type WebBackup struct {
	File      string    `json:"file"`
	SizeBytes int64     `json:"size_bytes"`
	CreatedAt time.Time `json:"created_at"`
}

WebBackup is one archive.

type WebCapabilities

type WebCapabilities struct {
	PHP     []string `json:"php"`
	Caddy   bool     `json:"caddy"`
	MariaDB bool     `json:"mariadb"`
}

WebCapabilities is what a node offers for web hosting (sent in hello).

type WebDatabase

type WebDatabase struct {
	Name      string `json:"name"`
	SizeBytes int64  `json:"size_bytes"`
}

WebDatabase is one database.

type WebDomain

type WebDomain struct {
	Name    string   `json:"name"`
	Aliases []string `json:"aliases,omitempty"`
	Root    string   `json:"root"` // document root relative to ~/sites/<name> ("public", "." …)
	TLS     string   `json:"tls"`  // "auto" (ACME) or "internal" (Caddy's local CA, for testing)
}

WebDomain is a site of the account: files in ~/sites/<name>/, served from Root inside it.

type WebLimits

type WebLimits struct {
	DiskMB     int `json:"disk_mb"`
	MemoryMB   int `json:"memory_mb"`
	CPUPercent int `json:"cpu_percent"`
	Processes  int `json:"processes"`
}

WebLimits are the account's resources.

type WebsiteApp

type WebsiteApp struct {
	ID     string `json:"id"`
	App    string `json:"app"`    // wordpress | git
	Domain string `json:"domain"` // one of the account's domains
	// WordPress: an existing database of the account.
	DBName     string `json:"db_name,omitempty"`
	DBPassword string `json:"db_password,omitempty"`
	// Git: HTTPS repository; a first deploy clones into the domain directory, later ones pull.
	Repo   string `json:"repo,omitempty"`
	Branch string `json:"branch,omitempty"`
}

WebsiteApp installs or deploys an application into a domain.

type WebsiteAppResult

type WebsiteAppResult struct {
	Message string `json:"message"`
}

WebsiteAppResult describes what happened.

type WebsiteBackup

type WebsiteBackup struct {
	ID     string `json:"id"`
	Action string `json:"action"`
	File   string `json:"file,omitempty"`
}

WebsiteBackup: action create, restore (File), delete (File) or list.

type WebsiteBackupList

type WebsiteBackupList struct {
	Backups []WebBackup `json:"backups"`
}

WebsiteBackupList lists backups in ~/backups.

type WebsiteDB

type WebsiteDB struct {
	ID       string `json:"id"`
	Action   string `json:"action"`
	Name     string `json:"name,omitempty"`
	Password string `json:"password,omitempty"`
}

WebsiteDB manages a database: action create (with password), password, delete, or list.

type WebsiteDBList

type WebsiteDBList struct {
	Databases []WebDatabase `json:"databases"`
}

WebsiteDBList lists the account's databases and their sizes.

type WebsiteSpec

type WebsiteSpec struct {
	ID          string            `json:"id"`
	Username    string            `json:"username"` // Linux user, also the database name prefix
	PHPVersion  string            `json:"php_version"`
	Domains     []WebDomain       `json:"domains"`
	Limits      WebLimits         `json:"limits"`
	PHP         map[string]string `json:"php,omitempty"` // php.ini overrides (allow-listed keys)
	Cron        []CronJob         `json:"cron,omitempty"`
	Suspended   bool              `json:"suspended"`
	BackupsKeep int               `json:"backups_keep,omitempty"`
}

WebsiteSpec describes a hosting account.

type WebsiteStatus

type WebsiteStatus struct {
	ID        string `json:"id"`
	PHPActive bool   `json:"php_active"`
	DiskBytes uint64 `json:"disk_bytes"`
	Home      string `json:"home"`
}

WebsiteStatus reports an account's state.

Jump to

Keyboard shortcuts

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