Repository navigation
Restarting Keryx
Changes to Python files don't take effect until Keryx restarts. Keryx may have made the change itself, so a restart reports its status and checks that Keryx came back.
uv run keryx restart --reason "picked up new code"
uv run keryx restart --status # how the last one wentkeryx restart asks the service manager to restart the installed unit. That's systemd on
Linux and launchd on macOS; SERVICE_MANAGER overrides the choice. Once Keryx is up
again, it phones you back by itself with a one-line status:
- how long it was down;
- the version before and after;
- which channels are listening;
- how many tasks the restart interrupted.
You can also ask on the phone: "restart yourself". A restart drops every call in progress, so a restart you ask for on a call waits until you've hung up.
| Flag | What it does |
|---|---|
--reason "…" |
a few words, which you hear back on the call |
--status |
print how the last restart went, and exit |
--force |
restart even while a call is in progress |
--no-callback |
restart quietly, with no call afterwards |
If a restart finishes during a call that's yours, the assistant gives the confirmation in that call. A call is yours if you gave the PIN, or if Keryx placed it to your number.
Otherwise, Keryx waits for the line to be free before it calls. If the line stays busy,
the call counts as failed. Keryx may be unable to place the call because Twilio is
unavailable, PUBLIC_HOST isn't set, or the phone channel is down. In that case, if
SMS_ENABLED=true, it texts you the summary. If texting is off or also fails, the result
is still available from keryx restart --status.
The restart command may fail, or nothing may happen within 20 seconds of it. Either way, the old process is still running, and it tells you:
- If a call that's yours is in progress, it says so in that call.
- Otherwise, if
SMS_ENABLED=true, it texts you. - If texting is off or fails, and no call is in progress, it rings you with a plain spoken alert. The alert gives only the headline: the restart didn't go through, and the old version is still running. It never rings while a call is in progress.
keryx restart --status has the details.
If nothing supervises the process, a restart refuses, because stopping would leave nothing
to start it again. That includes a keryx serve you started in a terminal on a machine
where the service is installed too. If you say "restart yourself" on a call to that copy,
it refuses rather than restart the installed one in its place. keryx doctor says which
case you're in. Running as a Service explains how the unit is found, and
Assumptions and Deployments explains what you give up without one.
The confirmation reads the running version from STATE_DIR/running-version, which
keryx serve writes when it starts. It doesn't ask git describe at the moment you
request the restart. The checkout changes under a running process. In the usual order
(edit, commit, then ask for the restart), the new commit is already on disk when you ask.
A check at that moment would compare the new commit with itself, and report that nothing
loaded. Only when the process starts are the checkout and the running code the same.
Keryx also checks that the service works, not just that it's running. Before the restart, it notes the current position in each service log. Afterwards, it reads the new entries, and reports any errors before the rest of the status.
If the service never starts again, it can't report the failure itself. This matters most after a code change that causes an import or startup error.
So just before a restart, Keryx starts a watchdog (keryx restart-watch) outside the
service, where the restart can't stop it. On Linux, it runs in a separate
systemd-run --user unit; on macOS, in a new session. The watchdog acts only if the
restart is still pending when its time runs out.
It then rings you with a plain spoken alert. It never uses a media stream, because the
process that serves media streams is the one that isn't running. If SMS_ENABLED=true, it
texts you the details first. The call still works with texting off. When you ask for a
restart, the assistant tells you which to expect: a text if texting is on, a call if it's
off.
Beyond that, Restart=always (systemd) or KeepAlive (launchd) brings the service back.
If the call never comes, look at systemctl --user status keryx and
STATE_DIR/logs/keryx.log (~/.local/state/keryx/logs/ by default).
An exit code of -15 can mean the restart worked. systemctl runs inside the service it
restarts, so the restart stops it with that code.
User documentation for Keryx.
For the threat model, see SECURITY.md;
for settings, the coding agents, and the tools, see docs/.
Apache-2.0, Copyright 2026 William Koch.
Setting up
Running it
Security and data
Tools and advanced features