Documentation
¶
Index ¶
Constants ¶
const ServiceGroupName = "_ephemerd"
ServiceGroupName is a dedicated primary group for the service user. Using a dedicated group instead of staff (gid 20 — the default group for every normal macOS account) keeps the runner process from inheriting group access to the many files on a typical Mac that are staff-group owned. Falls back to staff if the group can't be created.
const ServiceUserName = "_ephemerd"
ServiceUserName is the hidden macOS service account that native runner jobs execute as when no [runner.macos] user is configured. It is created lazily on first use and persists like other service accounts (_www, ...). Per-job user deletion is deliberately avoided: dscl/sysadminctl user deletion wedges opendirectoryd on modern macOS.
Variables ¶
This section is empty.
Functions ¶
func GenerateSandboxProfile ¶
func GenerateSandboxProfile(jobDir, dataDir, pemPath string, opts SandboxOptions) string
GenerateSandboxProfile returns a macOS sandbox profile that restricts the runner process. Paths are templated with the job-specific directories.
pemPath is the configured GitHub App private_key_path (may be empty). When set it is denied explicitly as a belt-and-suspenders measure on top of the broad /Users content deny below.
Two postures:
- opts.Strict == false (default): allow-by-default with an explicit deny-list. This is the previously-smoke-tested behavior and is emitted byte-for-byte as before when strict is off.
- opts.Strict == true (opt-in): deny-by-default with an explicit allow-list for the paths/services a GHA runner + toolchain needs, with the same tier-1/tier-2 denies layered on top. Much stronger, but not guaranteed complete for every toolchain — must be live-tested.
Types ¶
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes a GitHub Actions runner directly on the macOS host inside a per-job sandbox. Each job gets its own workspace, HOME, TMPDIR, keychain, and Homebrew prefix.
func New ¶
New creates a native macOS runner for a single job. It prepares the workspace directory structure but does not start the runner process.
pemPath is the configured GitHub App private_key_path (empty for PAT auth). It is threaded into the sandbox profile so the runner is denied read access to the operator's App private key even though the daemon runs as root with no HOME — see GenerateSandboxProfile.
func (*Runner) SetMaxProcesses ¶
SetMaxProcesses sets the ulimit -u process cap applied before the runner is exec'd. 0 disables the cap (unlimited).
func (*Runner) SetRunAsUser ¶
SetRunAsUser configures a non-root user to run the runner process as. The daemon (running as root) drops privileges via setuid/setgid when launching the runner. Strongly recommended when the daemon runs as root: without it, CI job steps execute as root on the host.
func (*Runner) SetSandboxStrict ¶
SetSandboxStrict enables the deny-by-default sandbox profile. Opt-in: the strict allow-list is not guaranteed to cover every toolchain and must be live-smoke-tested before it is turned on for a host.
func (*Runner) Start ¶
Start copies the runner binary, sets up the sandbox and environment, and launches the runner process.
type SandboxOptions ¶
type SandboxOptions struct {
// Strict switches from allow-by-default (deny-list) to deny-by-default
// (allow-list). Opt-in; default false keeps today's already-smoke-tested
// allow-by-default profile byte-for-byte identical.
Strict bool
// HomebrewPrefix is the host Homebrew prefix (e.g. /opt/homebrew) the
// runner uses read-only. Needed in the strict allow-list.
HomebrewPrefix string
// DeveloperDir is the resolved active Xcode / CLT path (xcode-select -p).
// Needed in the strict allow-list so clang/git shims work. May be empty.
DeveloperDir string
}
SandboxOptions carries the knobs GenerateSandboxProfile needs beyond the job/data/pem paths.