config

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClaimRetryToml

type ClaimRetryToml struct {
	// Enabled toggles the retry queue. Nil = default true; operators
	// disable by explicitly setting enabled = false.
	Enabled *bool `toml:"enabled"`

	// MaxAge is the total time budget from first failure to giving up.
	// Accepts Go duration strings ("90m", "1h30m"). Default 90m.
	MaxAge string `toml:"max_age"`

	// Schedule is the ordered backoff ladder. Each entry is the base
	// delay for one attempt; jitter is applied on top. Default is
	// {30s, 1m, 2m, 5m, 10m}. Use a shorter schedule for shorter
	// MaxAge, or a longer one to let jobs marinate through extended
	// outages.
	Schedule []string `toml:"schedule"`

	// Jitter is the +/- fraction applied to each delay (0.0-1.0).
	// Default 0.2 (+/-20%). Set 0 to disable jitter (useful in tests,
	// rarely useful in production).
	Jitter *float64 `toml:"jitter"`
}

ClaimRetryToml configures the scheduler's claim-retry queue.

Enabled defaults to true when the [runner.claim_retry] table is omitted (see RunnerConfig.ClaimRetryEnabled). Losing queued jobs to transient errors is almost never what an operator wants; set enabled = false to restore the pre-existing "log and drop" behavior.

type Config

type Config struct {
	GitHub      GitHubConfig      `toml:"github"`
	Forgejo     ForgejoConfig     `toml:"forgejo"`
	Gitea       GiteaConfig       `toml:"gitea"`
	GitLab      GitLabConfig      `toml:"gitlab"`
	Woodpecker  WoodpeckerConfig  `toml:"woodpecker"`
	Webhook     WebhookConfig     `toml:"webhook"`
	Containerd  ContainerdConfig  `toml:"containerd"`
	Network     NetworkConfig     `toml:"network"`
	VM          VMConfig          `toml:"vm"`
	Dind        DindConfig        `toml:"dind"`
	ModuleProxy ModuleProxyConfig `toml:"module_proxy"`
	Runtime     RuntimeConfig     `toml:"runtime"`
	Runner      RunnerConfig      `toml:"runner"`
	Metrics     MetricsConfig     `toml:"metrics"`
	Log         LogConfig         `toml:"log"`
}

func Load

func Load(path string) (*Config, error)

func (*Config) Logger

func (c *Config) Logger() *slog.Logger

func (*Config) Provider

func (c *Config) Provider() string

Provider returns the name of the first configured forge provider. Returns "github" by default for backward compatibility.

func (*Config) Providers

func (c *Config) Providers() []string

Providers returns all configured provider names. A provider is "configured" if its credential/URL fields are set.

type ContainerdConfig

type ContainerdConfig struct {
}

type DindConfig

type DindConfig struct {
	Enabled bool `toml:"enabled"` // mount /var/run/docker.sock with a fake Docker API

	// CachePruneInterval is how often the per-repo image cache pruner runs.
	// Accepts standard Go duration strings ("24h", "30m"). Set to 0 to
	// disable pruning entirely. Default 24h.
	CachePruneInterval time.Duration `toml:"cache_prune_interval"`

	// CacheMaxAge is the eviction threshold for cached image records:
	// any record whose ephemerd.io/last-accessed label (or UpdatedAt as
	// fallback) is older than this gets removed on the next prune pass.
	// Containerd's content GC then reclaims the unreferenced blobs.
	// Set to 0 to disable eviction (only empty-namespace cleanup runs).
	// Default 168h (7 days).
	CacheMaxAge time.Duration `toml:"cache_max_age"`

	// AllowPrivileged controls whether `docker run --privileged` (or
	// HostConfig.Privileged=true / HostConfig.CapAdd) from inside a job
	// is honored. When true, a sibling container can request the full
	// elevation stack (all caps, all devices, seccomp/apparmor off,
	// writable sysfs/cgroupfs) — needed for KIND clusters, nested
	// containerd, /dev/fuse-style mounts, etc. When false, such requests
	// are rejected with HTTP 403.
	//
	// SECURITY: a privileged sibling container is effectively root on
	// whatever host runs the containerd that backs dind. On Windows and
	// macOS hosts that backing containerd lives inside a managed Linux
	// VM (WSL2 / Hyper-V / Vz), so an escape only reaches the VM. On a
	// Linux host with no VM fence, an escape reaches the bare-metal host
	// — set this to false unless every workload is trusted.
	//
	// Use the pointer form so an empty/missing TOML key is
	// distinguishable from an explicit `allow_privileged = false`. See
	// ResolvedAllowPrivileged for the default policy.
	AllowPrivileged *bool `toml:"allow_privileged"`
}

DindConfig configures the fake Docker daemon mounted into job containers.

func (*DindConfig) DindCacheMaxAge

func (d *DindConfig) DindCacheMaxAge() time.Duration

DindCacheMaxAge returns the eviction threshold with the default applied when unset (or set to 0).

func (*DindConfig) DindCachePruneInterval

func (d *DindConfig) DindCachePruneInterval() time.Duration

DindCachePruneInterval returns the prune interval with the default applied when unset (or set to 0).

func (*DindConfig) ResolvedAllowPrivileged

func (d *DindConfig) ResolvedAllowPrivileged() bool

ResolvedAllowPrivileged returns whether privileged dind sibling containers are allowed, applying the secure default when the operator hasn't set the key explicitly.

Default policy: false on ALL platforms. Privileged is opt-in everywhere. A privileged sibling container is effectively root on whatever host runs the backing containerd, so shipping it on-by-default is unsafe even where a VM fence limits the blast radius (Windows/macOS) — an operator that needs it (KIND clusters, nested containerd, /dev/fuse) opts in explicitly with `allow_privileged = true`.

type ForgejoConfig

type ForgejoConfig struct {
	InstanceURL         string   `toml:"instance_url"`          // Forgejo instance URL (e.g., "https://codeberg.org")
	Token               string   `toml:"token"`                 // runner registration token from Forgejo admin
	Owner               string   `toml:"owner"`                 // org or user (empty = instance-level runner)
	Repos               []string `toml:"repos"`                 // limit to specific repos (empty = all)
	Labels              []string `toml:"labels"`                // runner labels (default: ["ubuntu-latest:docker://<job_image>"])
	DefaultImage        string   `toml:"default_image"`         // runner daemon image (default: "data.forgejo.org/forgejo/runner:12")
	DefaultImageLinux   string   `toml:"default_image_linux"`   // per-OS Linux runner image (wins over default_image when set)
	DefaultImageWindows string   `toml:"default_image_windows"` // per-OS Windows runner image
	JobImage            string   `toml:"job_image"`             // job execution image (default: "gitea/runner-images:ubuntu-24.04")
}

ForgejoConfig configures the Forgejo Actions provider. Set instance_url and token to enable Forgejo instead of GitHub. Uses forgejo-runner binary with one-job --handle mode.

Forgejo's runner daemon (DefaultImage) is Linux-only; setting DefaultImageWindows is allowed for completeness but no upstream Windows build of forgejo-runner exists today.

func (*ForgejoConfig) DefaultImageFor

func (f *ForgejoConfig) DefaultImageFor(os string) string

DefaultImageFor returns the provider-level default for the given OS.

type GitHubConfig

type GitHubConfig struct {
	// Authentication: either a PAT or GitHub App
	Token          string `toml:"token"`
	AppID          int64  `toml:"app_id"`
	InstallationID int64  `toml:"installation_id"`
	PrivateKeyPath string `toml:"private_key_path"`

	// Which org/user and repos to register runners for
	Owner string   `toml:"owner"`
	Repos []string `toml:"repos"`

	// Job discovery: polling interval (default "30s")
	PollInterval string `toml:"poll_interval"`

	// DefaultImage is the legacy single-image override (Linux only).
	// Kept for backward compatibility — prefer DefaultImageLinux /
	// DefaultImageWindows for new configs. When DefaultImageLinux is empty
	// and this is set, it's treated as the Linux default.
	// Linux fallback (when nothing is set): "ghcr.io/actions/actions-runner:latest".
	DefaultImage string `toml:"default_image"`

	// DefaultImageLinux is the provider-level default image for Linux jobs.
	// Per-repo entries in [runner.images.<repo>].linux win over this.
	DefaultImageLinux string `toml:"default_image_linux"`

	// DefaultImageWindows is the provider-level default image for Windows
	// jobs. Per-repo entries in [runner.images.<repo>].windows win over
	// this. Falls through to the runtime's host-matched servercore default
	// (pkg/runtime/image_windows.go) when unset.
	DefaultImageWindows string `toml:"default_image_windows"`
}

func (*GitHubConfig) DefaultImageFor

func (g *GitHubConfig) DefaultImageFor(os string) string

DefaultImageFor returns the provider-level default image for the given OS. Resolution: per-OS field → legacy DefaultImage (Linux only) → empty. Empty means "no provider default — let the runtime pick its OS-native fallback (e.g. mcr.microsoft.com/windows/servercore:ltsc20XX on Windows)".

func (*GitHubConfig) ParsedPollInterval

func (g *GitHubConfig) ParsedPollInterval() time.Duration

ParsedPollInterval returns the poll interval as a time.Duration.

type GitLabConfig

type GitLabConfig struct {
	InstanceURL         string   `toml:"instance_url"`          // GitLab instance URL (e.g., "https://gitlab.com")
	Token               string   `toml:"token"`                 // runner authentication token (glrt-xxx for GitLab 16+)
	Tags                []string `toml:"tags"`                  // runner tags for job matching
	DefaultImage        string   `toml:"default_image"`         // runner image (default: "ghcr.io/ephpm/runner-gitlab:latest")
	DefaultImageLinux   string   `toml:"default_image_linux"`   // per-OS Linux runner image (wins over default_image when set)
	DefaultImageWindows string   `toml:"default_image_windows"` // per-OS Windows runner image
}

GitLabConfig configures the GitLab CI provider. Set instance_url and token to enable GitLab instead of GitHub.

func (*GitLabConfig) DefaultImageFor

func (g *GitLabConfig) DefaultImageFor(os string) string

DefaultImageFor returns the provider-level default for the given OS.

type GiteaConfig

type GiteaConfig struct {
	InstanceURL         string   `toml:"instance_url"`          // Gitea instance URL (e.g., "https://gitea.example.com")
	Token               string   `toml:"token"`                 // runner registration token from Gitea admin
	Owner               string   `toml:"owner"`                 // org or user (empty = instance-level runner)
	Repos               []string `toml:"repos"`                 // limit to specific repos (empty = all)
	Labels              []string `toml:"labels"`                // runner labels (default: ["ubuntu-latest:docker://<job_image>"])
	DefaultImage        string   `toml:"default_image"`         // runner daemon image (default: "docker.io/gitea/act_runner:latest")
	DefaultImageLinux   string   `toml:"default_image_linux"`   // per-OS Linux runner image (wins over default_image when set)
	DefaultImageWindows string   `toml:"default_image_windows"` // per-OS Windows runner image
	JobImage            string   `toml:"job_image"`             // job execution image (default: "gitea/runner-images:ubuntu-24.04")
}

GiteaConfig configures the Gitea Actions provider. Set instance_url and token to enable Gitea instead of GitHub. Uses act_runner binary with --ephemeral mode.

func (*GiteaConfig) DefaultImageFor

func (g *GiteaConfig) DefaultImageFor(os string) string

DefaultImageFor returns the provider-level default for the given OS.

type LinuxVMToml

type LinuxVMToml struct {
	Enabled    bool   `toml:"enabled"`      // enable Linux VM for cross-OS Linux jobs
	CPUs       uint   `toml:"cpus"`         // virtual CPUs (default: 2)
	MemoryMB   uint64 `toml:"memory_mb"`    // memory in MB (default: 2048)
	DiskSizeGB uint64 `toml:"disk_size_gb"` // sparse disk size in GB (default: 50)
}

LinuxVMToml configures the long-running Linux VM for Linux jobs on Windows (Hyper-V) and macOS (Virtualization.framework) hosts.

type LogConfig

type LogConfig struct {
	Level        string `toml:"level"`
	Format       string `toml:"format"`        // "text" or "json"
	LogRetention string `toml:"log_retention"` // max age for job log files (e.g. "7d", "24h"); default "7d"

	// Writer overrides the log output destination. When nil, logs go to
	// stderr. Set by the Windows Service handler to route logs to the
	// Windows Event Log.
	Writer io.Writer `toml:"-"`
}

func (LogConfig) LogRetentionDuration

func (lc LogConfig) LogRetentionDuration() time.Duration

LogRetentionDuration returns the parsed log retention duration. Supports Go duration strings (e.g. "168h") and a "d" suffix for days (e.g. "7d"). Returns 7 days if the value is empty or invalid.

type MacOSRunnerConfig

type MacOSRunnerConfig struct {
	Mode      string            `toml:"mode"`       // "vm" (default) or "native"
	MaxNative int               `toml:"max_native"` // max concurrent native jobs (default 4)
	User      string            `toml:"user"`       // existing user for native runners (empty = ephemeral per-job user, recommended)
	Repos     map[string]string `toml:"repos"`      // "org/repo" -> "vm" or "native"

	// SandboxStrict switches the native sandbox profile from allow-by-default
	// (deny-list) to deny-by-default (allow-list). Default false. Strict mode
	// is a much stronger posture but requires enumerating every path a GHA
	// runner + toolchain legitimately touches, so it is opt-in and needs a
	// live smoke test on the target host before enabling.
	SandboxStrict bool `toml:"sandbox_strict"`

	// MaxProcesses caps the number of processes (ulimit -u) a native job may
	// spawn, providing fork-bomb defense. Default 2048 (generous — clang/php
	// fork heavily). 0 = unlimited (no ulimit set). Note: macOS has no
	// cgroups, so RAM and disk cannot be hard-capped on the native path; use
	// the VM path for untrusted memory/disk DoS resistance.
	MaxProcesses *int `toml:"max_processes"`
}

MacOSRunnerConfig controls macOS job routing. It lives under [runner] (not [vm.macos]) because native jobs don't involve VMs.

TOML shape:

[runner.macos]
mode = "vm"         # default mode: "vm" or "native"
max_native = 4      # max concurrent native jobs
# user = "ciuser"   # optional: existing user for native runners.
#                   # Default (unset): an ephemeral hidden user is
#                   # created per job and deleted on cleanup.

[runner.macos.repos]
"ephpm/*"           = "native"  # all repos in org
"ephpm/secret-repo" = "vm"     # except this one (exact wins over wildcard)
"someuser/ephemerd" = "vm"     # fork stays on VM

func (*MacOSRunnerConfig) ModeForRepo

func (m *MacOSRunnerConfig) ModeForRepo(repo string) string

ModeForRepo returns "native" or "vm" for the given repo. Resolution order:

  1. Exact match on "org/repo"
  2. Wildcard match on "org/*"
  3. Short-name fallback: if repo has no "/", match any "org/<repo>" key
  4. Top-level mode
  5. Default: "vm"

The short-name fallback exists because some providers (GitHub polling) currently emit event.Repo as just the repo name without the org prefix. Config keys should always use "org/repo" format for disambiguation.

func (*MacOSRunnerConfig) ResolvedMaxNative

func (m *MacOSRunnerConfig) ResolvedMaxNative() int

ResolvedMaxNative returns the max concurrent native macOS jobs, defaulting to 4 if unset or non-positive.

func (*MacOSRunnerConfig) ResolvedMaxProcesses

func (m *MacOSRunnerConfig) ResolvedMaxProcesses() int

ResolvedMaxProcesses returns the ulimit -u value for native jobs. Unset (nil) defaults to 2048. An explicit 0 means unlimited (return 0 so the caller skips the ulimit). A negative value is treated as unlimited.

func (*MacOSRunnerConfig) StrictSandbox

func (m *MacOSRunnerConfig) StrictSandbox() bool

StrictSandbox reports whether deny-by-default sandbox mode is enabled.

type MacOSVMToml

type MacOSVMToml struct {
	// DiskImage is an optional path to a pre-installed macOS VM disk
	// (produced by `ephemerd vm setup-macos` or an operator-supplied
	// restore of an Apple IPSW). If empty, ephemerd downloads the latest
	// Apple-signed IPSW on first boot and installs stock macOS into
	// <data_dir>/vm/macos/base.img. Distinct from the OCI base image
	// overlaid per job — that's fetched from the job's image label.
	DiskImage     string `toml:"disk_image"`
	CPUs          uint   `toml:"cpus"`           // CPUs per VM (default: 4)
	MemoryMB      uint64 `toml:"memory_mb"`      // memory per VM in MB (default: 8192)
	MaxConcurrent int    `toml:"max_concurrent"` // max simultaneous macOS VMs (default: auto-detected from host CPUs)
}

MacOSVMToml configures per-job macOS VMs. macOS jobs always run in a per-job VM on darwin hosts — there's no other way on Apple Silicon — so there's no enable/disable toggle. On non-darwin hosts this block is ignored.

type MetricsConfig

type MetricsConfig struct {
	Enabled bool   `toml:"enabled"`  // enable /metrics endpoint (default false)
	Port    int    `toml:"port"`     // listen port (default 9090)
	Path    string `toml:"path"`     // metrics path (default "/metrics")
	TLSCert string `toml:"tls_cert"` // TLS certificate path (optional)
	TLSKey  string `toml:"tls_key"`  // TLS private key path (optional)
	// ContainerStatsInterval is how often per-container resource samples are
	// taken (CPU, memory). Used both by the host's local sampler ticker and as
	// the cadence the host requests from the in-VM Dispatch StreamContainerStats
	// stream. Default 10s.
	ContainerStatsInterval string `toml:"container_stats_interval"`
}

MetricsConfig configures the Prometheus metrics endpoint. Disabled by default. Set enabled = true to expose /metrics.

func (MetricsConfig) ParsedContainerStatsInterval

func (m MetricsConfig) ParsedContainerStatsInterval() time.Duration

ParsedContainerStatsInterval returns the configured per-container sampling interval, applying the default (10s) when unset. Falls back to the default on parse error rather than failing the daemon — the metric series simply won't update if this is misconfigured, which is not worth aborting startup.

type ModuleProxyConfig

type ModuleProxyConfig struct {
	Enabled  bool   `toml:"enabled"`  // enable Go module caching proxy
	Port     int    `toml:"port"`     // listen port on bridge gateway (default 8082)
	Upstream string `toml:"upstream"` // upstream proxy URL (default "https://proxy.golang.org")
	Cleanup  bool   `toml:"cleanup"`  // wipe cache on shutdown (default true)
}

ModuleProxyConfig configures the Go module caching proxy. When enabled, ephemerd runs a local GOPROXY on the bridge gateway that caches module downloads. Containers receive GOPROXY env var automatically.

type NetworkConfig

type NetworkConfig struct {
	Subnet string `toml:"subnet"` // container subnet (auto-selected if empty)
	MTU    int    `toml:"mtu"`    // bridge MTU (auto-detected from host if 0)
}

NetworkConfig configures container networking.

type OrphanSweepToml

type OrphanSweepToml struct {
	// Enabled toggles the sweep. Nil = default true; operators disable
	// by explicitly setting enabled = false.
	Enabled *bool `toml:"enabled"`

	// Grace is how long a dispatched runner may sit without being
	// assigned a job before it is destroyed and deregistered. Accepts
	// Go duration strings ("10m", "1h"). Default 10m.
	Grace string `toml:"grace"`
}

OrphanSweepToml configures the scheduler's orphaned-runner sweep.

Enabled defaults to true when the [runner.orphan_sweep] table is omitted. The sweep only acts in webhook mode (in polling mode there are no in_progress events, so ephemerd cannot tell an orphaned runner from a busy one) and only for providers that report runner assignments (GitHub).

type RunnerConfig

type RunnerConfig struct {
	MaxConcurrent int      `toml:"max_concurrent"`
	ExtraLabels   []string `toml:"extra_labels"`
	DefaultImage  string   `toml:"default_image"`

	// Images maps repo → OS → image. TOML shape:
	//
	//   [runner.images.ephemerd]
	//   linux   = "ephpm/ephemerd:runner-ci-linux-amd64"
	//   windows = "ephpm/ephemerd:runner-ci-windows"
	//
	// A repo can specify just one OS — the others fall through to the
	// provider per-OS default and then the runtime fallback.
	Images map[string]map[string]string `toml:"images"`

	JobTimeout      string            `toml:"job_timeout"`
	ShutdownTimeout string            `toml:"shutdown_timeout"`
	Windows         WindowsRunnerToml `toml:"windows"`
	MacOS           MacOSRunnerConfig `toml:"macos"`

	// ClaimRetry controls the in-memory retry queue for jobs whose
	// initial claim / provision attempt fails with a transient error
	// (rate limit, 5xx, network). GitHub does not re-deliver
	// workflow_job webhooks, so without this queue any queued job that
	// hit an API blip at claim time would be lost until human
	// intervention.
	ClaimRetry ClaimRetryToml `toml:"claim_retry"`

	// OrphanSweep controls teardown of runners that were dispatched for
	// a job but never observed picking one up. GitHub assigns JIT
	// runners to ANY queued job with matching labels, so runner
	// lifecycle is keyed to the observed assignment; a runner whose
	// intended job went elsewhere and that never got a job of its own is
	// destroyed after the grace window.
	OrphanSweep OrphanSweepToml `toml:"orphan_sweep"`
}

func (*RunnerConfig) ClaimRetryEnabled

func (r *RunnerConfig) ClaimRetryEnabled() bool

ClaimRetryEnabled reports whether the claim retry queue should run. Defaults to true (retries on) when the table is omitted or Enabled is nil; operators opt out with `enabled = false`.

func (*RunnerConfig) ClaimRetryJitter

func (r *RunnerConfig) ClaimRetryJitter() float64

ClaimRetryJitter returns the retry backoff jitter fraction. Defaults to 0.2 (+/-20%). Values outside [0,1] are clamped to the default.

func (*RunnerConfig) ClaimRetryMaxAge

func (r *RunnerConfig) ClaimRetryMaxAge() time.Duration

ClaimRetryMaxAge returns the retry queue give-up threshold. Defaults to 90 minutes when unset or unparseable.

func (*RunnerConfig) ClaimRetrySchedule

func (r *RunnerConfig) ClaimRetrySchedule() []time.Duration

ClaimRetrySchedule returns the retry backoff ladder. Defaults to {30s, 1m, 2m, 5m, 10m} when unset. Unparseable entries are dropped; if all entries fail to parse, the default is returned.

func (*RunnerConfig) ImageForRepo

func (r *RunnerConfig) ImageForRepo(repo string) string

ImageForRepo is the legacy helper kept for callers that haven't migrated. Prefer ImageForRepoOS. Returns the Linux image override (if set), then DefaultImage. Empty when neither is set.

func (*RunnerConfig) ImageForRepoOS

func (r *RunnerConfig) ImageForRepoOS(repo, os string) string

ImageForRepoOS returns the per-repo, per-OS image override, or empty if no override is configured for that combination. Caller falls back to the provider default and then the runtime default.

func (*RunnerConfig) OrphanSweepEnabled

func (r *RunnerConfig) OrphanSweepEnabled() bool

OrphanSweepEnabled reports whether the orphaned-runner sweep should run. Defaults to true when the table is omitted or Enabled is nil; operators opt out with `enabled = false`.

func (*RunnerConfig) OrphanSweepGrace

func (r *RunnerConfig) OrphanSweepGrace() time.Duration

OrphanSweepGrace returns how long a dispatched runner may remain unassigned before the sweep destroys it. Defaults to 10 minutes when unset or unparseable.

func (*RunnerConfig) ParsedJobTimeout

func (r *RunnerConfig) ParsedJobTimeout() time.Duration

ParsedJobTimeout returns the job timeout as a time.Duration.

func (*RunnerConfig) ParsedShutdownTimeout

func (r *RunnerConfig) ParsedShutdownTimeout() time.Duration

ParsedShutdownTimeout returns the shutdown timeout as a time.Duration.

type RuntimeConfig

type RuntimeConfig struct {
	Rlimits RuntimeRlimits `toml:"rlimits"`
}

RuntimeConfig configures behavior of the per-job container runtime — things that apply to the OCI spec rather than to a specific subsystem like dind or networking.

type RuntimeRlimits

type RuntimeRlimits struct {
	// Nofile is RLIMIT_NOFILE (max open file descriptors). Both soft
	// and hard get set to this value. Default 1024 (containerd default).
	Nofile int64 `toml:"nofile"`
	// Nproc is RLIMIT_NPROC (max processes/threads for the container's
	// user). Both soft and hard get set to this value. Default 1024.
	Nproc int64 `toml:"nproc"`
}

RuntimeRlimits sets POSIX resource limits (RLIMIT_*) on each runner container's OCI spec. Defaults match containerd's built-in OCI spec (nofile=1024, nproc=1024) so an empty config is a no-behavior-change.

Set higher when CI workloads need more file descriptors or processes than containerd's defaults allow. Common case: a build tool calling `ulimit -n 2048` to raise its open-file ceiling. That fails with "Operation not permitted" if the container's hard limit is 1024 — raising the hard limit needs CAP_SYS_RESOURCE, which we deliberately don't grant. Setting nofile higher here lets the same `ulimit` call succeed without granting the capability, because lowering is always allowed and the OCI hard limit is now generous.

func (RuntimeRlimits) Resolved

func (r RuntimeRlimits) Resolved() RuntimeRlimits

Resolved returns the rlimits with defaults filled in for any unset (zero or negative) field. Always returns positive values so callers can blindly emit OCI rlimit entries.

type VMConfig

type VMConfig struct {
	// CrossPlatform enables macOS and Windows VM support. Default true.
	// Set to false for platforms like Gitea/Forgejo that only support
	// Linux runners — this skips macOS image pulls and Windows VM setup.
	CrossPlatform *bool       `toml:"cross_platform"`
	Linux         LinuxVMToml `toml:"linux"`
	MacOS         MacOSVMToml `toml:"macos"`
}

VMConfig configures virtual machines for cross-OS job execution.

func (*VMConfig) CrossPlatformEnabled

func (v *VMConfig) CrossPlatformEnabled() bool

CrossPlatformEnabled returns whether macOS/Windows VM support is enabled. Defaults to true when not set.

type WebhookConfig

type WebhookConfig struct {
	Secret  string `toml:"secret"`   // webhook HMAC secret (auto-generated for managed tunnels; required for "external")
	Port    int    `toml:"port"`     // listen port for health endpoint (default 8080)
	TLSCert string `toml:"tls_cert"` // TLS certificate path (direct TLS, no tunnel)
	TLSKey  string `toml:"tls_key"`  // TLS private key path
	Tunnel  string `toml:"tunnel"`   // "none" (default, polling), "external" (unmanaged ingress), "localtunnel", "ngrok", or "cloudflared"
	// ExternalURL is the public base URL of the externally-managed tunnel
	// (e.g. https://mac.tricorder.cc). When set with tunnel="external",
	// ephemerd registers each tracked repo's webhook to
	// <external_url>/webhook/<provider> using the secret. Ignored for managed
	// tunnels (they use the tunnel's own URL) and for polling.
	ExternalURL      string `toml:"external_url"`
	TunnelURL        string `toml:"tunnel_url"`         // localtunnel: self-hosted server URL
	NgrokAuthtoken   string `toml:"ngrok_authtoken"`    // ngrok auth token (or use NGROK_AUTHTOKEN env)
	TunnelMaxRetries int    `toml:"tunnel_max_retries"` // max consecutive reconnect failures before falling back to polling (default 5)

	// cloudflared: tunnel = "cloudflared". Ephemerd runs cloudflared as a
	// managed subprocess bound to its own lifetime (child gets SIGTERM on
	// parent exit). The tunnel and its DNS record must be provisioned in
	// Cloudflare beforehand — ephemerd only runs the client. The token
	// authenticates and identifies which tunnel to connect.
	CloudflaredToken    string `toml:"cloudflared_token"`    // tunnel run token (or use CLOUDFLARE_TUNNEL_TOKEN env)
	CloudflaredHostname string `toml:"cloudflared_hostname"` // public FQDN of the tunnel (e.g. "runner.example.com"); required for GitHub webhook registration
	CloudflaredVersion  string `toml:"cloudflared_version"`  // pinned cloudflared release (e.g. "2026.6.1"); defaults to a known-good version if empty

	// Pool marks this instance as one member of a pool of ephemerd nodes
	// sharing a single public webhook URL (e.g. cloudflared tunnel replicas
	// behind one hostname). In pool mode webhook registration is
	// adopt-or-create (an existing hook with the same URL is converged, not
	// duplicated), the hook is never deregistered on shutdown (pool-mates
	// still need it), and the startup stale-hook sweep is skipped (it cannot
	// tell a pool-mate's live hook from a stale one). Requires an explicit
	// shared webhook.secret — every pool member must present the same one.
	Pool bool `toml:"pool"`

	// ReconcileInterval controls the webhook-mode reconcile sweep: how often
	// ephemerd re-runs the catch-up poll to pick up jobs that got stranded.
	//
	// This is a LAST-RESORT backstop only for genuinely DROPPED webhook
	// deliveries (network/tunnel loss where GitHub's own redelivery also
	// missed us). The common stranding case — a fungibly-reassigned runner
	// leaving its dispatched job queued — is now healed instantly and
	// event-drivenly by the scheduler on runner exit, without polling. So this
	// runs at a low frequency: empty = default 30m; a zero/negative duration
	// disables it entirely (relying purely on the event-driven path + GitHub's
	// delivery retries).
	ReconcileInterval string `toml:"reconcile_interval"`
}

WebhookConfig configures webhook delivery and tunnel providers. By default, ephemerd uses polling (tunnel = "none"). Set tunnel = "localtunnel", "ngrok", or "cloudflared" for ephemerd to create and manage a tunnel and auto-register the GitHub webhook. Set tunnel = "external" when a tunnel is provided by something else: ephemerd then serves the webhook receiver and disables polling, but does not create a tunnel or register the webhook — that is owned externally, so a matching secret is required.

func (*WebhookConfig) ResolvedReconcileInterval

func (w *WebhookConfig) ResolvedReconcileInterval() time.Duration

ResolvedReconcileInterval returns the webhook-mode reconcile sweep interval: 30m by default (empty or unparseable), the parsed value otherwise, and 0 (disabled) only when explicitly set to a zero/negative duration.

type WindowsRunnerToml

type WindowsRunnerToml struct {
	MemoryMB uint64 `toml:"memory_mb"` // memory in MB (default: 4096)
	CPUs     uint64 `toml:"cpus"`      // virtual CPUs (default: 2)
}

WindowsRunnerToml configures resource limits for Hyper-V isolated Windows runner containers. Without limits Hyper-V containers default to ~1 GB RAM, which is too small for MSVC + parallel cl.exe builds.

func (WindowsRunnerToml) CPUCount

func (w WindowsRunnerToml) CPUCount() uint64

CPUCount returns the CPU count, applying the default if unset.

func (WindowsRunnerToml) MemoryBytes

func (w WindowsRunnerToml) MemoryBytes() uint64

MemoryBytes returns the memory limit in bytes, applying the default if unset.

type WoodpeckerConfig

type WoodpeckerConfig struct {
	ServerURL   string `toml:"server_url"`   // Woodpecker server gRPC URL (e.g., "woodpecker.example.com:9000")
	AgentSecret string `toml:"agent_secret"` // shared secret for agent authentication
}

WoodpeckerConfig configures the Woodpecker CI provider. Set server_url and agent_secret to enable Woodpecker instead of GitHub. Woodpecker requires a forge backend (Gitea/Forgejo) for repo management; ephemerd manages the agent lifecycle, not the server.

Jump to

Keyboard shortcuts

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