How it works
What the CLI knows about a machine
setup, sync and doctor already connect to a machine. While they are
there, they read what it runs — uname, /etc/os-release, sw_vers on a
Mac, where ansible-playbook is — in one extra command, and keep the
answer in <config>/state/machines/<name>.json.
devmachine machines show <name> prints it without connecting, and
devmachine --format json machines show <name> gives it to a script or an
agent under observed. devmachine machines rm <name> deletes the file
with the machine, so a new machine that reuses the name does not inherit
what the old one ran.
Why it is not in config.yml
config.yml says what you want. This file says what a command saw, and
the next sync reads the machine again. Mixing the two would make a
reinstalled server look like a change to your configuration, and an
autocommit would record it as one. So the file lives under state/,
which devmachine setup git keeps out of the repository. A repository
made before this release gets the .gitignore line the next time you run
devmachine setup git.
It lives in the configuration directory, like known_hosts, and not in
~/.local/state, because a machine name is unique only inside one
configuration: two configurations can each have a machine called vps.
Why it is written only when it changes
The macOS app watches the configuration directory and reloads on every
change. A doctor that rewrote the file on each run would make the app
reload for nothing. So the file is written only when something other than
the time differs, and observed_at is when the machine was first seen as
the file describes it — not the last time somebody looked. The file is
written to a temporary name in the same folder and then renamed, so a
reader never sees half of it.
What reads it
runputspath_prefixin front ofPATH. A plain SSH command on a Mac gets/usr/bin:/bin:/usr/sbin:/sbinand nothing else, soport,brewand the Python they installed are not found without it. On Linux the list is empty andrunsends the command as it is.- Every other command the CLI runs on a Mac by name gets the same prefix,
plus
/Applications/Tailscale.app/Contents/MacOSlast, where Tailscale’s app keeps its CLI:login’s command (gh auth login,tailscale up), a network package’sjoinandself_name, andexpose’scaddy validateandcaddy reload.exposewrites the prefix inside the script it pipes tosudo, becausesudoreplaces thePATHit was started with. syncrefuses, before it changes anything, a package whoseplatformsleave out the machine’s system — see troubleshooting. A machine nobody has read yet is not refused: its firstsyncreads it.- On a Mac reached over SSH,
synccalls Ansible byansible_playbook, the absolute path the package manager package’s bootstrap reported duringsetup. A MacPorts install can name itansible-playbook-3.14, which noPATHwould find. On Linux and on your own computer it isansible-playbookfromPATH, as before. - An agent reads
observedbefore it writes aruncommand, so it usespacmanon Arch,apton Debian and Ubuntu, andbreworporton a Mac. A distribution based on one of them, such as Manjaro, gets its base’sos_familyandpkg_mgrand keeps its own name indistribution.
A command never fails because this file could not be written: it is a by-product of a read that already worked.