Skip to content

env_clear() on spawn leaves supervised modules without HOME/XDG_RUNTIME_DIR, so ck cannot discover the daemon from inside a module's process tree #104

Description

@iceteaSA

Found deploying 0.18.2 to this box. Not a blocker — the daemon and all four modules are healthy — but it changes behaviour for every tool invoked inside a supervised module's process tree, and the failure it produces points at the wrong thing.

Symptom

$ ck daemon
no usable subc connection file found; tried: /tmp/subc-1000.connection.json (not found)

One candidate. The daemon's connection file is at /run/user/1000/subc-connection.json, and ~/.local/share/cortexkit/run/subc-connection.json symlinks to it. Neither was tried.

The daemon is running and healthy on the same box:

$ XDG_RUNTIME_DIR=/run/user/1000 ck daemon
daemon 0.18.2 · pid 2815040 · up 52s · 8 clients · no frame drops in the last 10 minutes

Cause

c97d3473 ("mason: add daemon fleet logging", 09-15) added command.env_clear() to spawn_child. I verified it is not in 856e26f7, the build this box was running before the upgrade — it arrived with 0.18.2.

The daemon now spawns modules with a cleared environment, re-adding only SystemRoot (Windows), the module's configured spec.env minus capture-log keys, and SUBC_MODULE_ID/nonce. Measured inside a tool shell running under a supervised module after the bounce:

HOME               <UNSET>
XDG_RUNTIME_DIR    <UNSET>
TMPDIR             <UNSET>
USER               <UNSET>

connection_file::discovery_candidates reads XDG_RUNTIME_DIR and HOME to build its list. With both unset it falls back to env::temp_dir() alone — hence the single /tmp candidate.

Why this is worth a look rather than a workaround

The stated intent in the comment is narrow: stop an operator's ambient CK_LOG leaking into an otherwise unconfigured module. That is a good goal. env_clear() achieves it by removing everything, and the collateral is the whole POSIX environment.

Three consequences, in increasing order of how long they'd take to diagnose:

  1. ck cannot find a daemon running on the same machine, from inside any module's process tree. Every module here is served that way, so this is the common case, not an edge one.
  2. The error names a path the file has never lived at. "tried: /tmp/subc-1000.connection.json" reads as "the daemon didn't write its file", when the truth is "this process cannot see where it was written". A reader debugging this looks at the daemon first.
  3. Anything else a module spawns inherits itgit without ~/.gitconfig, cargo without CARGO_HOME, ssh, python user dirs. Those degrade quietly rather than erroring.

I nearly filed this as a ck discovery regression, which is the wrong diagnosis and worth recording: ck had worked bare all day and stopped immediately after I deployed a new one, so the timing pointed hard at the binary I had just swapped. The discriminator was restoring a single variable — with XDG_RUNTIME_DIR set, the new ck finds the daemon instantly. The binary was never the variable; its caller's environment was.

Options

I don't think this is mine to pick — it trades isolation against reachability, and the isolation was deliberate:

  1. Allowlist the standard environment — re-add HOME, XDG_RUNTIME_DIR, TMPDIR, USER, PATH after clearing, keeping the scrub for everything else. Preserves the CK_LOG intent exactly; loses nothing the comment was guarding against.
  2. Denylist instead of clear — remove CK_LOG and the capture-log keys specifically, inherit the rest. Smallest behavioural delta from pre-0.18.2, but it re-opens the "next leaky variable nobody thought of" hole env_clear closed.
  3. Keep the scrub, make discovery independent of it — have the daemon pass the connection-file path to children (it already passes SUBC_ARG), and have ck consult a fixed well-known location. Fixes reachability without weakening isolation, but doesn't help git/cargo/ssh.
  4. Keep as-is and document it — the scrub is correct and callers inside a module tree are expected to pass --subc. Defensible, but the error message should then name the real locations rather than only the temp-dir fallback.

My lean is (1), with the error message improved regardless of which is chosen: whatever the candidate list ends up being, a discovery failure that lists one path the file never occupies will send the next reader to the wrong component. If the scrub stays, that message is the part that makes it survivable.

Happy to take whichever you rule.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions