Skip to content

Give a session a HOME when the node was handed none - #115

Merged
gmpassos merged 2 commits into
masterfrom
fix/session-home
Sep 13, 2026
Merged

gmpassos merged 2 commits into
masterfrom
fix/session-home

Conversation

@gmpassos

Copy link
Copy Markdown
Contributor

omnyshell exec worker-1 -- echo $HOME comes back empty on a node installed
as a service.

Neither the node nor the session is at fault. systemd hands a system unit
PATH, LANG and even USER, but sets HOME only if the unit asks — and
sessions inherit the node's environment faithfully, so there was nothing to
inherit. Read straight off a running node's agent process:

INVOCATION_ID=…   JOURNAL_STREAM=10:107861   SYSTEMD_EXEC_PID=115
LANG=C.UTF-8      PATH=/usr/local/sbin:…     USER=root
OMNYSERVER_HOME=/var/lib/omnyserver

No HOME, and the unit's only Environment= is OMNYSERVER_HOME.

Why it matters more than it looks

It fails quietly. cd ~ goes nowhere and says nothing — you stay in the
node's working directory — ~/… stops expanding, and anything keeping state
under a home directory (git, ssh, package managers) writes somewhere else.

Running this repo's own omnyshell local with HOME unset left a
.omnyshell/history/ in the checkout: omnyshellHome() was falling back to
.. Same gap, seen from the other side.

The fix

resolveUserHome asks the password database when the environment is silent:

  1. HOME / USERPROFILE when set and non-empty — always the answer.
  2. USER / LOGNAME looked up in /etc/passwd. systemd sets USER, so this
    is the common case.
  3. The process's own uid, from /proc/self/status — the real key, and the
    only one left in a bare container, which sets neither variable.
  4. /root for uid 0 where there is no password database to read at all.
  5. Otherwise nothing. A wrong home is worse than none: it would send those
    same tools to a directory that is not the user's.

Both node shell backends now pass the session environment through withUserHome
— which fills HOME in only when the node process has none, and never overrides
one already set. expandUserHome and omnyshellHome resolve through the same
path.

Verified

On Linux, where it actually bites — a container with neither HOME nor USER:

process HOME: null      process USER: null
resolveUserHome():      /root
withUserHome({TERM}):   {TERM: xterm, HOME: /root}
expandUserHome(~/x):    /root/x

macOS hides this — zsh sets HOME for itself at startup, so a Debian node
running sh is where it shows. Worth knowing before trying to reproduce
locally.

16 new unit tests (the environment, the passwd lookup by name and by uid, an
empty HOME, a malformed passwd line, an unknown uid, and the refusal to
guess), with the process environment and both file paths injectable so none of
it depends on the machine running the suite. 689 unit tests pass on macOS and
Linux; dart analyze clean.

v1.57.0 is tagged, so this carries the bump to 1.57.1pubspec.yaml and
lib/src/version.dart, pinned together by version_test.dart — plus its
changelog section.

Not in this PR

The OmnyServer side is worth fixing too: the unit it generates sets only
OMNYSERVER_HOME, and adding HOME there would cover formulas as well —
apt-get, git and dart pub run with that same environment. This change does
not touch them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KdG8bDrddXkEkzgPq23D6q

`omnyshell exec worker-1 -- echo $HOME` came back empty on a node installed as
a service. The node is not at fault and neither is the session: systemd hands a
system unit PATH, LANG and even USER, but sets HOME only if the unit asks, and
sessions inherit the node's environment faithfully. So there was nothing to
inherit.

It failed quietly, which is the worse part: `cd ~` went nowhere and said
nothing — landing you in the node's working directory — `~/…` stopped
expanding, and anything keeping state under a home directory wrote elsewhere.
Running this repo's own `omnyshell local` with HOME unset left a
`.omnyshell/history/` in the checkout, which is the same gap seen from the
other side.

resolveUserHome now asks the password database when the environment is silent:
by USER/LOGNAME first, then by the process's own uid — the real key, and the
only one left in a bare container, which sets neither. Root falls back to /root
where there is no password database to read. Where there is genuinely no
answer it says so rather than guessing: a wrong home would send git, ssh and
package managers somewhere that is not the user's.

Both node shell backends now fill HOME into a session's environment when the
node process has none, never overriding one already set; expandUserHome and
omnyshellHome resolve through the same path.

Verified on Linux, where it actually bites: in a container with neither HOME
nor USER, resolveUserHome answers /root, a session environment gains
HOME=/root, and `~/x` expands to /root/x. macOS hides this — zsh sets HOME for
itself at startup — so a Debian node running sh is where it shows.

v1.57.0 is tagged, so this carries the bump to 1.57.1 and its changelog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdG8bDrddXkEkzgPq23D6q
@codecov

codecov Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

yaml ^3.1.4, and dev-only test ^1.32.0 and dependency_validator ^5.0.6.
No source change: the suite passes unmodified against these versions
(689 unit tests), `dart analyze` is clean and dependency_validator finds
nothing.

Noted in the 1.57.1 section this branch already opened, since that version is
not yet released.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdG8bDrddXkEkzgPq23D6q
@gmpassos
gmpassos merged commit befcbc3 into master Sep 13, 2026
6 checks passed
@gmpassos
gmpassos deleted the fix/session-home branch September 13, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant