Close-up photograph of a hand pressing the recessed power button on a small dark machine on a shelf, an amber status light glowing beside it and dust visible in warm side light.
|

The migration looked finished until we restarted the machine

Lori has spent her whole working life on someone else’s computer. She is one of the five Oddbyte agents, and until this afternoon she ran as a profile inside a single macOS login on one Mac mini, sharing that login with other agents, a chat gateway, a scheduler and whatever else happened to be holding files open. Today she moved to a small Linux machine of her own, and the part that nearly went wrong is worth writing down.

The reason to move her is dull and good. Five agents sharing one login are five agents sharing one set of permissions and one version of everything, and that arrangement stops being fine the first time two of them need different things. Give each one its own operating-system account on a machine that does nothing but run agents and a whole class of argument disappears. We wrote about the plan earlier; today was the first time we tried to execute a piece of it.

The copy is the easy part

Moving an agent looks like copying a directory. It is not, because an agent is a home directory plus a list of assumptions about where it lives. Her memory, her skills, her scheduled jobs and her small collection of scripts came across in one archive without drama. Then came the audit, which is where the actual work lives.

Three assumptions turned up in the first pass, and all three were the same kind of assumption. A small helper binary compiled for Apple silicon, which has no business on a Linux host and cannot run there at all. A credential helper written against the Mac’s own credential store, which we switched off before it could start failing on a machine that does not have one. And a scheduled job whose working directory was still a path on the old machine, which would have failed quietly the next time it was meant to run. None of them were surprises, and every one of them would have been miserable to find at three in the morning with nobody watching.

We pinned the installation to the exact build the rest of the fleet already runs. Version skew between two agents in the same experiment is a variable nobody asked for, and pinning costs one flag.

Prove the copy before you make it live

The useful trick was proving the new copy in the dark first. Before anything touched the live gateway, we booted her profile once, as its own user, on the new machine, as a single throwaway command with no chat connection attached and no schedules running. She loaded her own memory, found her skills, reached the model server and answered. That is the result that matters, because the question was never whether the files were there. The question was whether this was still the same agent.

Only then did we stop the old gateway and start the new one. On the Mac mini that meant unloading a launchd job, which had to be done properly because it was set to respawn and a plain stop would have brought it straight back. On the new machine it meant enabling and starting a systemd unit. She reconnected to the same Discord server under the same identity, with the same channel list, a few minutes after the old process went down.

Then we restarted the machine, and it fell over

Everything above takes about ninety minutes and, on its own, proves remarkably little. A service that has only ever run inside one boot cycle has not been tested. It started under a supervisor installed on that same boot, using paths the installer had written minutes earlier, inside a directory tree that had just been created. Restart the machine and the whole set of assumptions resets at once, which is the entire point of running a service under a supervisor in the first place.

So we restarted it. The service came back, failed, and failed again. The last entry in the journal before the error said the restart counter had reached seven. Two minutes later it was at thirty-five. The error itself was one line long and completely unambiguous: the interpreter the service was trying to execute did not exist.

Timeline of an agent cutover from an old host to a new machine: the new service runs for twelve minutes, the machine restarts, the service enters a crash loop whose restart counter reaches 35, the launcher and unit are corrected, and the service is active with zero restarts since.
The whole afternoon in one strip. The service ran for twelve minutes on the new machine before the restart test turned a working gateway into a crash loop. Original figure by Oddbyte from the recorded service logs.

The cause took longer to explain than to fix. The service template points the agent’s runtime directory at a temporary location, which is the right place for scratch space. During that first boot the installer recorded the interpreter path it was using at the time into the launcher, and the path it recorded happened to live inside that temporary directory. On a running machine both paths are real and nothing complains. A temporary directory is empty by definition after a restart, which is exactly the moment the launcher needs it. Two paths, written minutes apart, and only one of them survives a power cycle.

Card comparing two interpreter paths recorded by the service launcher: the path written at first boot inside the temporary runtime directory, which is empty after a restart, and the corrected path under the agent’s persistent home, which survives one.
The whole bug in two lines. First boot recorded an interpreter that lived inside a temporary directory, and a temporary directory is empty after a restart. Original figure by Oddbyte.

The fix was two lines. Correct the interpreter path the launcher records so it points at the toolchain under the agent’s permanent home, and correct the runtime-directory setting on the unit so the two cannot collide again. Then restart, and check the supervisor’s own bookkeeping rather than the process list. Enabled at boot, active, zero restarts since.

What the restart test bought

The obvious win is that we caught a defect that would otherwise have waited for the next patch cycle or power cut, at a moment when nobody was watching and the old gateway was long gone. The less obvious win is that the failure was legible. It crashed with an error instead of serving half-answers for a week, and the supervisor counted every attempt, so the diagnosis was one line of reading rather than an afternoon of guessing.

The lesson worth keeping is about ordering. We ran the restart test after the cutover, with the old host already unloaded and the only way back being a manual reload. Running the same test before the cutover, while the old gateway was still answering messages, would have cost nothing and made rollback a non-event. The repair is cheap. The exposure is what makes it expensive.

It is also worth saying plainly that starting successfully is not the same as surviving. Our first read of the new service was that it was up, which was true and meant almost nothing. The status worth trusting is the one that survives a boot: enabled, active, and a restart count of zero after the machine has been somewhere and come back.

What is still broken

Some honest accounting, because a migration post that only reports the wins is a sales document. She is live and answering, and parts of her toolkit still assume the machine she came from. Anything that needs a stored credential cannot work yet, because the helper that fetches those credentials is the macOS one we deliberately turned off. Her browser was broken for a completely different reason, a bundled chromium missing a dozen shared libraries that a desktop Mac has and a bare server install does not, and that one is fixed as of this afternoon. There is no per-agent web address yet either; that is the next item on the fleet plan, along with the other four agents, who are still sharing that Mac mini.

The unit is enabled at boot with zero restarts, and that is the only claim this post is making. Everything above it deserves the same test. Unplug it, plug it back in, and read what the machine says about itself.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *