Context
Cordial is two processes.cordial-shell is the launcher — a single-instance
GtkApplication on the session bus, which is what lets a roblox-player: link
clicked in a browser reach the launcher that is already open instead of starting
a second one. cordial-run is the client: it loads libroblox.so, holds the
profile’s flock (ADR-012) and owns its own window.
Two questions were open, and they turn out to be the same question.
“Closing the launcher closes Cordial completely” was reported against the
Flatpak build on 2026-09-13. “Is launching the binary the proper way, or is it
a hack?” was asked in the same breath.
The reported bug is real, and the obvious explanation for it is wrong
The natural reading is that a Flatpak sandbox tears down when itscommand:
process exits, taking the client with it. That was measured and it is false.
A marker process backgrounded inside flatpak run --command=sh io.github.luohoa97.Cordial, with the sandbox’s own main process then exiting,
was still alive on the host seconds later with bwrap still hosting it — both
as a plain child and under setsid. The sandbox is not the mechanism.
The mechanism is a pipe.
launch.rs gives the child Stdio::piped() on both streams and pumps them on
reader threads, so that the crash page can quote what the client printed. The
read ends of those pipes belong to the launcher process. Close the launcher
window, and with no window left the GtkApplication quits, the process exits,
both read ends close — and the next println! inside cordial-run panics:
window.rs said that under ADR-012 “quitting
the launcher while a client runs is the ordinary case”. That described an
intention. It had never been true.
Decision
The launcher holds itself alive for as long as a client it started is running.GtkApplication::hold() is taken when launch::spawn succeeds and
the guard is dropped by the SIGCHLD child watch when the client exits, at
which point the application quits by itself. Closing the launcher window now
closes a window; it does not end the process.
The client stays a child process, spawned with Command. That is not a
hack, and the alternatives are worse:
- A
fork/execdaemon that reparents to init would keep the client alive past a launcher crash, and would cost the crash page — the captured output is the single most useful thing in a bug report here — and theSIGCHLDwatch that drives it. flatpak-spawn --hostis arbitrary command execution on the host and is refused outright by the manifest’s own reasoning; see the header ofpackaging/io.github.luohoa97.Cordial.yml.- One process contradicts ADR-002, which keeps the shell buildable without the engine on purpose, and would put a 1.5 GB engine load in the same address space as the launcher’s WebKit view.
SIGCHLD watch is the right shape. What was missing was the lifetime rule that
makes it safe, and that is what this ADR adds.
What this does not fix
A launcher that crashes still takes the client with it, by the same pipe. The hold makes the ordinary path safe; it does not make the client independent. The defence in depth is forcordial-run to survive a broken stdout rather than
panic on it — Rust’s println! panics on EPIPE because std sets SIGPIPE to
SIG_IGN at startup, so the write error surfaces rather than the signal. The
better shape is for the client to narrate into a log file inside its profile and
for the launcher to tail that, which would also give users a log to attach to a
bug report. Neither is done here, and neither should be claimed.
Deep links still do not reach a client that is already running. The launcher
is single-instance, so the link reaches the running launcher correctly.
ADR-035 adds automatic matching through
the configured secret backend; unmatched links and unavailable sessions still
wait for the button. Either path starts a new client
— which ADR-012’s lock refuses if that profile is already open. Routing a join
into a live engine needs an IPC into cordial-run that does not exist outside
the development control surface (ADR-019). That is a separate decision and
wants its own ADR rather than being folded into this one.