node

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: MIT Imports: 41 Imported by: 0

Documentation

Overview

Package node is the podium-node daemon: it holds the node's identity, keeps one bidirectional stream to the control plane, and turns each Assign into a container run whose events are batched, buffered and replayed until the server acks them.

Index

Constants

View Source
const (
	DefaultServer                  = "http://127.0.0.1:8080"
	DefaultTransport               = "local"
	DefaultDataDir                 = "/var/lib/podium-node"
	DefaultMaxTasks                = 4
	DefaultImageCacheHighWatermark = 0.80
	DefaultMetricsListen           = "127.0.0.1:9091"
)

Config defaults. Mirrored in docs/node-setup.md; keep the two in step.

View Source
const (
	// TransportLocal dials a loopback server with a shared bearer token.
	TransportLocal = "local"
	// TransportTailnet embeds the node's own Tailscale device (tsnet) and dials the control
	// plane's MagicDNS name over it. The node listens for nothing.
	TransportTailnet = "tailnet"
	// TransportHost dials over the machine's existing tailscaled instead of embedding a device.
	TransportHost = "host"
)

The transports podium-node understands.

View Source
const DefaultConfigPath = "/etc/podium/node.yaml"

DefaultConfigPath is where podium-node looks for its config file when --config is not given. A missing file there is not an error: the daemon is fully configurable by environment.

View Source
const DefaultReleaseBaseURL = "https://github.com/podium-ade/podium/releases/download"

DefaultReleaseBaseURL is where a released archive lives. GoReleaser publishes <base>/<tag>/podium_<version>_<os>_<arch>.tar.gz alongside <base>/<tag>/checksums.txt.

View Source
const MinRedactableSecret = 8

MinRedactableSecret is the shortest value worth searching a log stream for. Below it the false positives cost more than the protection is worth: a two-byte secret would redact every occurrence of those two bytes in every line the task ever prints.

View Source
const ReplayBufferBytes = 8 << 20

ReplayBufferBytes bounds one task's unacked events. Past it the oldest log chunks are dropped — never exited, finished or error, which are what the server needs to move the task out of running.

Variables

This section is empty.

Functions

func FetchBinary

func FetchBinary(ctx context.Context, opts UpgradeOptions, stagePath string) error

FetchBinary downloads the release archive, checks its SHA-256 against the release's own checksums.txt, extracts podium-node from it and writes the result to stagePath with mode 0755. It never touches the running binary: swapping is the caller's move, and it is a rename so that it is atomic.

The checksum is verified before anything is extracted. An archive that does not appear in checksums.txt is refused rather than trusted — a release whose manifest does not mention a file is a release that was tampered with or built wrong, and either way it is not the one that was signed.

func SaveIdentity

func SaveIdentity(dataDir string, id Identity) error

SaveIdentity writes the identity 0600. It is written before the first stream so a crash never strands a node key that only the server-side hash remembers.

func StagePath

func StagePath(dest string) string

StagePath is where FetchBinary should write, given the destination.

func SwapBinary

func SwapBinary(stagePath, dest string) error

SwapBinary replaces dest with the staged file. Rename is atomic within a filesystem and works on a binary that is currently executing: the running process keeps the inode it started from and the next start picks up the new one.

Types

type Config

type Config struct {
	// Server is the control plane base URL, PODIUM_NODE_SERVER.
	Server string `yaml:"server"`
	// Transport is PODIUM_NODE_TRANSPORT: dev only in MVP-0.
	Transport string `yaml:"transport"`
	// DataDir holds the node identity and each task's state, PODIUM_NODE_DATA_DIR.
	DataDir string `yaml:"data_dir"`
	// Labels are advertised at enrollment and used for scheduling, PODIUM_NODE_LABELS
	// (comma-separated).
	Labels []string `yaml:"labels"`
	// MaxTasks is the concurrency budget the scheduler assigns against,
	// PODIUM_NODE_MAX_TASKS. Zero would mean this node never gets work.
	MaxTasks int `yaml:"max_tasks"`
	// EnrollToken is consumed on the first run only, PODIUM_NODE_ENROLL_TOKEN.
	EnrollToken string `yaml:"enroll_token"`
	// LocalToken is the shared bearer token of the local transport, PODIUM_NODE_LOCAL_TOKEN.
	LocalToken string `yaml:"local_token"`
	// TSAuthKey is the *Tailscale* auth key, PODIUM_NODE_TS_AUTHKEY or TS_AUTHKEY: reusable,
	// pre-approved, tagged tag:podium-node. It is read on the first run only, and it is a
	// different thing from EnrollToken, which is Podium's own single-use secret.
	// SENSITIVE: never log it.
	TSAuthKey string `yaml:"ts_auth_key"`
	// TSHostname overrides the Tailscale device name, PODIUM_NODE_TS_HOSTNAME. Empty derives
	// it from the machine's hostname.
	TSHostname string `yaml:"ts_hostname"`
	// ImageCachePrune turns on the LRU image cache prune, PODIUM_NODE_IMAGE_CACHE_PRUNE.
	// It is off by default and must be turned on deliberately: a developer running a node
	// on a laptop shares that Docker engine with the rest of their work, and no amount of
	// disk pressure justifies deleting an image out from under it. See docs/node-setup.md.
	ImageCachePrune bool `yaml:"image_cache_prune"`
	// ImageCacheHighWatermark is the disk-usage fraction above which pruning starts, when
	// pruning is enabled at all: PODIUM_NODE_IMAGE_CACHE_HIGH_WATERMARK.
	ImageCacheHighWatermark float64 `yaml:"image_cache_high_watermark"`
	// MetricsListen serves /healthz, /readyz and /metrics, PODIUM_NODE_METRICS_LISTEN.
	MetricsListen string `yaml:"metrics_listen"`
	// DockerHost overrides the engine endpoint, PODIUM_NODE_DOCKER_HOST. Empty means
	// the usual DOCKER_HOST / docker context / default socket resolution.
	DockerHost string `yaml:"docker_host"`
	// ExitOnDrain makes the daemon exit 0 once a drain has been requested and the last
	// running task has finished, PODIUM_NODE_EXIT_ON_DRAIN or --exit-on-drain. It is the
	// upgrade path: a supervisor restarts the process on the new binary. Without it a
	// drained node stays connected and idle, which is what an operator taking a machine
	// out of service for maintenance wants.
	ExitOnDrain bool `yaml:"exit_on_drain"`
	// AllowPrivilegedSidecars honours a spec's `privileged: true` on a sidecar,
	// PODIUM_NODE_ALLOW_PRIVILEGED_SIDECARS or --allow-privileged-sidecars. It is off by
	// default because such a container is root on this machine's kernel: none of the
	// sandbox every other task runs under applies to it, and a spec author must not be
	// able to opt into that from a YAML file. It is what a docker-in-docker sidecar
	// needs. Turn it on only on a machine dedicated to that, and pair it with a label
	// (--labels privileged) so the specs that need it are the only ones that land here.
	// See docs/security.md.
	AllowPrivilegedSidecars bool `yaml:"allow_privileged_sidecars"`
}

Config is the whole of podium-node's configuration: /etc/podium/node.yaml overlaid by PODIUM_NODE_* environment variables, which in turn are overlaid by flags.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig is the configuration a node with no file and no environment runs with.

func LoadConfig

func LoadConfig(path string) (Config, error)

LoadConfig reads path (or DefaultConfigPath when path is empty) over the defaults and then applies the environment. A file that was explicitly asked for must exist; the default one need not.

func (*Config) TSStateDir

func (c *Config) TSStateDir() string

TSStateDir is where the node's own Tailscale device identity lives. It sits under the data dir because it is exactly as durable as identity.json: lose it and the node re-registers as a new Tailscale device, leaving a ghost in the admin console.

func (*Config) Validate

func (c *Config) Validate() error

Validate reports the first thing that would stop the daemon from starting, in words an operator can act on. It also creates the data directory, because "is it writable" has no answer that does not involve trying.

type HostFacts

type HostFacts struct {
	Hostname      string
	CPUCores      int32
	MemoryMB      int64
	DockerVersion string
}

HostFacts is what the node tells the server about the machine it runs on. It is gathered once, at startup, and reported at enrollment and in every Hello.

type Identity

type Identity struct {
	NodeID string `json:"node_id"`
	// SENSITIVE: never log. The server stores only its SHA-256.
	NodeKey string `json:"node_key"`
}

Identity is what Enroll returned, persisted 0600. NodeKey is handed out exactly once by the server, so losing this file means enrolling again with a fresh token.

func LoadIdentity

func LoadIdentity(dataDir string) (Identity, bool, error)

LoadIdentity reads the identity from dataDir. The second result is false when there is none yet, which is the signal to enroll.

type Node

type Node struct {
	// contains filtered or unexported fields
}

Node is the daemon: one Docker executor, one identity, one stream at a time, and one replay buffer per task in flight.

func New

func New(ctx context.Context, cfg Config, logger *slog.Logger) (*Node, error)

New validates the configuration, connects to Docker, resolves the node's identity — enrolling on the first run — and prepares the stream client. It does not connect the stream; Run does that.

func (*Node) Close

func (n *Node) Close() error

Close releases the Docker client and leaves the tailnet. Running containers are deliberately left alone: the next incarnation adopts them.

func (*Node) MetricsAddr

func (n *Node) MetricsAddr() string

MetricsAddr is the bound address of the health and metrics server, valid once Run has started it.

func (*Node) NodeID

func (n *Node) NodeID() string

NodeID is the enrolled identity's ID.

func (*Node) Run

func (n *Node) Run(ctx context.Context) error

Run starts the health endpoint, adopts anything the previous incarnation left behind, and then holds a stream to the control plane until ctx is cancelled — reconnecting with exponential backoff, and re-sending Hello and every unacked event each time.

type UpgradeOptions

type UpgradeOptions struct {
	// Version is the release tag, "v0.3.1". The leading v is optional.
	Version string
	// BaseURL defaults to DefaultReleaseBaseURL. A test — or an air-gapped mirror — points
	// it somewhere else.
	BaseURL string
	// GOOS and GOARCH default to this process's.
	GOOS, GOARCH string
	// HTTPClient defaults to http.DefaultClient.
	HTTPClient *http.Client
}

UpgradeOptions describes one release to fetch. Everything has a default except Version.

func (UpgradeOptions) ArchiveName

func (o UpgradeOptions) ArchiveName() string

ArchiveName is the release archive for these options, matching .goreleaser.yaml's name_template. It is exported because the installer script and the docs quote it.

Directories

Path Synopsis
Package docker runs Podium tasks as containers on a local Docker engine and streams ordered lifecycle events for them.
Package docker runs Podium tasks as containers on a local Docker engine and streams ordered lifecycle events for them.

Jump to

Keyboard shortcuts

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