FOUNDER DIARY · CHAPTER 06 / 10

Shipping Is a Feature: A Desktop Shell, a Sidecar and a Package

Chapter 6 of the Apexia founder diary: wrapping Fabric in a Tauri desktop shell, the sidecar handshake and shutdown, Debian packaging bugs, and a quiet summer.

PUBLISHED
NOVEMBER 6, 2026
READ
6 MIN READ
BY
THE FOUNDER · APEXIA

Chapter 5 ended on April 18, 2026, with one interface left: a React front end talking to a Python service. That is a web application with a developer setup behind it, not an installed program. A tool that only its author can start is a demo. This chapter covers April 25 to mid May, when the work turned to making Fabric something an engineer could install, and the stretch after it, when mainline went quiet until July 12.

There is no physics hook in this chapter. The technical subject is software: how a desktop shell starts a backend it does not control, how it knows the backend is ready, how it stops it cleanly, and why the packaging around all of that is product work rather than an afterthought.

Plan 9: a shell around a service

On April 25 Plan 9 appeared: a Tauri desktop wrapper. Like the plans before it, it was written as a spec and an implementation plan before it was built. The design is a Rust shell that owns the application window and spawns the Python backend as a sidecar process. The front end stays what it was; the shell gives it a window, and gives the backend a parent that starts it and stops it.

April 25 carried 33 mainline commits, the only active day in a week that otherwise had none. It was the last big day of the spring.

A sidecar sounds simple: start a process, point the window at it. Two moments make it harder than that, and Plan 9 named both, with integration tests for each.

Starting: a handshake, not a guess

When the shell spawns the backend, the process exists long before it can answer. A Python service has to load its interpreter, import its modules and open its listener. If the window starts making requests in that interval, the first thing the user sees is an error from a program that is about to work. A fixed delay is the usual shortcut, and it is wrong in both directions: too long on a fast machine, too short on a slow one or on a first launch.

The shell does a health-check handshake instead. It asks the backend whether it is up, and keeps asking, with retry, until the backend answers. The window does not depend on how long startup usually takes. It depends on the backend saying it is ready.

Stopping: SIGTERM, then SIGKILL

The other moment is the end. When the window closes, the backend has to go with it. A sidecar that outlives its window keeps holding whatever it held: open files, a listening port, a half-written project. The next launch then finds the port taken or the file locked, and the user is left with a process they never started by hand and cannot see.

Plan 9 shuts the sidecar down in two steps. On Linux, where the Debian package lives, these are POSIX signals. First the shell sends SIGTERM, the polite request: the process can catch it, finish what it is doing, flush and close its files, and exit on its own terms. If it has not exited after a grace period, the shell sends SIGKILL, which the process cannot catch or ignore; the operating system ends it. The first signal is for correctness, the second is the guarantee. Either one alone is a mistake. SIGKILL alone never lets the backend clean up. SIGTERM alone trusts a process that may be stuck to stop itself.

  1. 01Spawn the Python backend as a child of the Rust shell.
  2. 02Handshake: check its health, retrying, until it answers or the shell reports failure.
  3. 03Run: the window talks to the backend it knows is ready.
  4. 04Stop: SIGTERM and a grace period, then SIGKILL if the process is still there.

The handshake and the shutdown each got integration tests. In a development setup these paths are easy to skip, because the server is usually started and stopped by hand. For an installed app they are exactly the paths every installed user goes through on every launch.

May 5: the app its installer could not see

A shell that works from the source tree is still not a product. On May 5 two latent bugs turned up that were hiding the Tauri shell from the Debian package. Part of the fix was a path: the sidecar path had to match the dh_virtualenv layout. dh_virtualenv packages the Python environment into its own directory tree, so a path to the backend that is correct in a development checkout is not where the backend lives on an installed system.

That class of bug is why packaging is product work. None of it shows up in unit tests or in a developer setup, because the developer setup is the one environment where every path is already right. It only shows up when the software is installed the way a user would install it, and then it is the whole experience: nothing else in the tool matters if it does not start.

The same day the release plan was finalized "for ship", with Apexia Fabric branding and an install guide, the repository was renamed, and v0.2.0 was tagged. On May 12 CI was changed to pre-build the front end and the Tauri shell, so the Debian build could not skip them. That last change is the lesson of May 5 turned into a rule: the build that produces the package is not allowed to assume the pieces are already there.

Then a quiet stretch

After that, mainline history goes quiet. May closed with 25 commits, all between May 4 and May 13. June has none on mainline. The next mainline commit is July 12, after 59 days without one. The figure puts that gap in context against the rest of the year, week by week.

Seven consecutive weeks, from the week of May 18 to the week of June 29, have no mainline commits at all. The spring work sits on the left: 162 commits in the week of v0.1.0 and 121 the week after. The two biggest weeks of the whole history come later, 193 in the last week of July and 270 in the last week of September, and they belong to later chapters.

Mainline is not the whole story. In June a UI redesign was built on a side branch: 26 commits, with a verification gate that passed on June 12 with a build, 80 unit tests and 11 end-to-end tests. It did not merge. The design-system port in July replaced it. I will not invent a reason for the pause on mainline; the history does not give one, and this diary only says what the history says.

What the history does show is that the pause came after a release, not in the middle of one. The shell, the handshake, the shutdown, the package fix and the CI guard were all in place by May 13. When work resumed in July, it resumed on a tool that could be installed.

Next: naming it Fabric

Chapter 7 picks up on July 12. Within two weeks the Fabric design system was vendored into the app, the whole interface was reskinned, and on July 25 the tool took the name Apexia Fabric at version 0.3.0, with a four-builder release matrix publishing the installers. You can install the result today from Apexia Fabric.

NEXT STEP

Install it, do not just demo it.

Apexia Fabric is free to download as a beta and runs entirely in simulation, with no hardware attached. If you have an idea that needs a first version people can actually install, that is what our MVP builds are for.

TAGSFounder DiaryApexia FabricTauriDesktop PackagingDebianRelease Engineering