A GPU provider sent us this, from a Mac with an M4 Max:
$ TOKEN=node_… ~/.paralon/paralon-agent install
zsh: killed TOKEN=node_… ~/.paralon/paralon-agent install
$ ~/.paralon/paralon-agent logs
zsh: killed ~/.paralon/paralon-agent logs
Every invocation, killed before it printed a line. Even logs, which does
nothing but run tail -f. No crash report in Console, nothing in our logs,
because the process never lived long enough to open a socket.
We checked the obvious first. The binary in the release was intact: 6,678,354 bytes, the size curl had reported on their machine, arm64 Mach-O, ad-hoc code signature present, SHA-256 matching the published one. Three other Macs were running that exact file. So the file was fine and the machine could run it. What could make the kernel refuse?
What "zsh: killed" means
killed is the shell reporting that the process received SIGKILL. When it
happens at launch, before main() runs, it is not your program crashing; it
is the kernel declining to execute the file. On Apple Silicon the usual
reason is code signing: every executable must carry a valid signature, even
an ad-hoc one, and a binary whose signature does not match its contents is
killed on exec.
The important detail is how macOS checks that. The signature is validated per file, and the result is cached and tied to the file's identity on disk, its vnode. Modify the file's bytes without creating a new file, and the cached verdict and the contents disagree. The kernel does not re-verify; it kills.
How the provider got there
They had installed the agent before. Our one-line install did this:
curl -L https://…/paralon-agent-darwin-arm64 -o ~/.paralon/paralon-agent
chmod +x ~/.paralon/paralon-agent
curl -o onto a path that already exists opens the existing file and
overwrites its contents in place. Same inode, new bytes. The downloaded
binary was perfect; the file it was written into was no longer the file the
kernel had validated. Every launch after that: killed.
Our agent's own self-update never hit this, which is why we had not seen it. It downloads to a temporary file and renames it over the old one. A rename swaps in a new inode with its own fresh signature check. Same bytes, different outcome.
Confirm it in ten seconds
codesign -vv ~/.paralon/paralon-agent
A healthy binary prints valid on disk and satisfies its Designated Requirement. One in this state prints an error about the signature or the
resources being modified. And for completeness, that the file is what you
think it is:
file ~/.paralon/paralon-agent # Mach-O 64-bit executable arm64
shasum -a 256 ~/.paralon/paralon-agent # compare with the published hash
If the hash matches the release and codesign still complains, it is this
problem, not a corrupt download.
The fix
For the machine in front of you:
rm -f ~/.paralon/paralon-agent
curl -L https://…/paralon-agent-darwin-arm64 -o ~/.paralon/paralon-agent
chmod +x ~/.paralon/paralon-agent
Delete, then download. The new file is a new inode and gets a clean verification.
For your install script, so nobody hits it again, either delete before
downloading, or download to a temporary name and mv it into place:
curl -L "$URL" -o "$DEST.tmp" && chmod +x "$DEST.tmp" && mv "$DEST.tmp" "$DEST"
Ours now does the first, and also stops a running agent before touching the file:
mkdir -p ~/.paralon && (~/.paralon/paralon-agent stop 2>/dev/null; rm -f ~/.paralon/paralon-agent) \
&& curl -L … -o ~/.paralon/paralon-agent && chmod +x ~/.paralon/paralon-agent
The change is in the command every provider copies from their dashboard, so a second install of the Mac agent is now the same as a first one.
When it is not this
Two lookalikes, quickly ruled out:
bad CPU type in executable: an arm64 binary on an Intel Mac. A different message, anduname -msaysx86_64.- Gatekeeper's dialog ("cannot be opened because the developer cannot be
verified"): that is for apps launched from Finder, or files carrying the
quarantine attribute, which a
curldownload does not set. It shows a window, notkilled.
And if codesign -vv is clean, the hash matches, and it is still killed,
look at endpoint security software on the machine; some of it kills
unsigned or ad-hoc-signed binaries by policy, and the symptom is identical.
The general lesson
Never overwrite a running or previously verified executable in place on
macOS. curl -o, cp onto the target, cat > file, dd: all keep the
inode. rm then write, or write elsewhere then mv: both create a new one.
It costs nothing and it removes an entire class of "works on my Mac" support
tickets.


