shellenv

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package shellenv reads the environment a user's login shell sets up, so a background process started from a desktop launcher (with a bare PATH) or from a coding agent (with the agent's environment) sees the same tools and settings as the user's terminal.

The shell runs as an interactive login shell in its own session, without a terminal, with a timeout. It is given only the session's own variables (see Session) and a default PATH, so its output depends on the user's startup files rather than on whoever started baum. If it prints nothing, a login shell that is not interactive is asked instead. A program printed between two random markers reports the resulting environment as JSON, so whatever the startup files print is ignored. Values are never logged.

Windows has no login shell. There the result is the inherited environment with the PATH a new logon would get: the system Path followed by the user's, from the registry. A tool installed after the starting process was (winget, an installer that edits the user Path) is found without signing out, and so is one only the user's Path names when an agent started baum.

Index

Constants

View Source
const DefaultAgentRetry = time.Minute

DefaultAgentRetry is how long Agent waits after a failed login shell run before asking again.

View Source
const DefaultTimeout = 10 * time.Second

DefaultTimeout bounds one resolution; slow startup files fall back to the inherited environment.

View Source
const ResolvingVariable = "BAUM_RESOLVING_ENVIRONMENT"

ResolvingVariable is set to 1 in the shell's environment, so startup files can skip slow or interactive work while baum reads the environment.

Variables

View Source
var ErrUnsupported = errors.New("login shell environment is not supported on this platform")

ErrUnsupported is returned where there is neither a login shell nor a registry to ask.

View Source
var Kept = []string{
	"USER", "LOGNAME", "SHELL", "LANG", "TZ",
	"SSH_AUTH_SOCK", "DBUS_SESSION_BUS_ADDRESS", "DISPLAY", "WAYLAND_DISPLAY",
	"GIT_SSH_COMMAND", "GIT_SSH",
	"PATH",
	"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "no_proxy", "all_proxy",
	"SSL_CERT_FILE", "SSL_CERT_DIR", "NODE_EXTRA_CA_CERTS",
	"GITHUB_TOKEN", "GH_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "GH_HOST", "GH_CONFIG_DIR", "GH_PATH",
	"ANTHROPIC_API_KEY", "CLAUDE_CONFIG_DIR", "CODEX_HOME",
}

Kept lists the variables a background baum process keeps, besides BAUM_* and LC_*: the session, the tools' PATH, network settings, GitHub tokens and the backend CLIs' configuration directories. Nothing else is passed on.

View Source
var Locations = []string{
	"HOME", "XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_CACHE_HOME", "XDG_RUNTIME_DIR",
	"TMPDIR", "USERPROFILE", "APPDATA", "LOCALAPPDATA",
}

Locations decide where baum keeps its files: the daemon's lock, socket and job journal, the registry, caches and stores. They always come from the starting process, never from the login shell, because the daemon resolved its lock and socket from them before reading the shell; a location only the shell sets would put its job journal somewhere another daemon could also use.

View Source
var Session = []string{
	"HOME", "USER", "LOGNAME", "SHELL", "TMPDIR", "LANG", "TZ",
	"XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_STATE_HOME", "XDG_CACHE_HOME", "XDG_RUNTIME_DIR",
	"SSH_AUTH_SOCK", "DBUS_SESSION_BUS_ADDRESS", "DISPLAY", "WAYLAND_DISPLAY",
}

Session lists the inherited variables the shell is given: where the user's files are and how to reach the desktop session's agents. Everything else comes from the shell's startup files.

Functions

func Arguments

func Arguments(shell string) []string

Arguments are the flags that make shell an interactive login shell running one command, which the caller appends.

func Merge

func Merge(inherited []string, resolved map[string]string) []string

Merge builds a background process's environment. The Locations only ever come from inherited, and stay unset when it lacks them. With a resolved login shell environment, every other kept variable comes from it, and one only the inherited environment has is dropped, except that Unix PATH appends inherited directories after the login shell directories. Without a resolved environment (resolved is nil), kept variables come from inherited.

func Names

func Names(env []string) []string

Names returns the variable names of env, for logs that must not hold values.

func PrintEnv

func PrintEnv(w io.Writer, marker string) error

PrintEnv writes this process's environment as a JSON object between two copies of marker. It is what the shell runs.

Types

type Agent

type Agent struct {
	// Resolve returns the login shell's SSH_AUTH_SOCK.
	Resolve func(ctx context.Context) (string, error)
	// Retry spaces login shell runs after a failure; zero is
	// DefaultAgentRetry.
	Retry time.Duration
	// Now is a seam for tests.
	Now func() time.Time
	// contains filtered or unexported fields
}

Agent keeps a long-lived process's SSH agent socket usable. The socket in its environment goes stale when the session that provided it ends (an ssh login closes, an agent restarts); a stale socket is replaced by the login shell's, or dropped, so a fetch fails clearly instead of using it.

The shell is asked only when a socket this process had has gone stale: a user without an agent never pays for a shell run. An answer without a usable socket is remembered; a failed run is retried after Retry. Callers share one run and never wait on each other otherwise.

func (*Agent) Socket

func (a *Agent) Socket(ctx context.Context) string

Socket is the agent socket to use, "" for none. A usable SSH_AUTH_SOCK is kept; a stale one is replaced in this process's environment by the login shell's, or removed.

type Options

type Options struct {
	// Shell is the login shell; empty uses $SHELL from Environ, then the
	// user database.
	Shell string
	// Printer is the program, and its leading arguments, that prints the
	// environment with PrintEnv; Resolve appends the marker. Empty runs this
	// executable as "daemon print-env".
	Printer []string
	// Timeout bounds the shell; zero means DefaultTimeout.
	Timeout time.Duration
	// Environ is the inherited environment the session variables are taken
	// from; nil uses os.Environ().
	Environ []string
}

Options configure Resolve.

type Result

type Result struct {
	Shell string
	Env   map[string]string
	// Fallback is why the interactive login shell printed no environment,
	// when a login shell that is not interactive printed Env instead.
	Fallback string
}

Result is a resolved environment and the shell that produced it.

func Resolve

func Resolve(ctx context.Context, opts Options) (Result, error)

Resolve runs the login shell and returns the environment it set up. When the interactive shell prints no environment, a login shell that is not interactive is asked within the same timeout (see Result.Fallback). A shell that is slow, or prints no environment either way, is an error; callers fall back to the inherited environment.

Jump to

Keyboard shortcuts

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