0060 named the gap and did not close it: everywhere else an init runs the launcher at boot, and Android grants neither an init to register with nor anything worth supervising, because a supervisor would be killed alongside what it supervises. Closed by narrowing what is required rather than building something. A host is resident or episodic, and both are hosts. Being killed by the platform is disconnection, which 0036 already made ordinary -- and every mechanism an episodic host needs already exists because it was built for laptops that close. A partial host can join a mesh and cannot be the first node, since every bootstrap step is a shape it refuses. Its bundle says so. Two consequences that are easy to miss: last-heard-from means much less on an episodic host, so a healthy phone reads as a dead server unless the reader knows which kind it is; and a declaration may take a long time to land, which makes 0058's outstanding-versus-failed distinction load-bearing. Still open, and in that order: what an Android node is FOR, and only then how it is started.
5.5 KiB
status, date, deciders, reconstructed, extends
| status | date | deciders | reconstructed | extends |
|---|---|---|---|---|
| accepted | 2026-08-28 | jochen | false | 0060-the-host-is-built-per-operating-system.md |
62. A host may be episodic, and being killed is ordinary
Context
ADR 0060 makes a partial host a real thing —
Android implements file, directory and action and refuses the rest — and leaves one gap
open, named but not closed:
Being STARTED on Android is not solved by this file, and it is the real gap. Everywhere else an init runs the launcher at boot. Here the equivalent is the app framework — a foreground service, or something under Termux — both of which the system may kill when it wants memory.
There is no way to keep a process running on an ordinary Android device. Registering with init needs root and an unlocked bootloader. A foreground service is the sanctioned alternative and is still subject to the system reclaiming memory, to Doze, and to whatever the manufacturer added on top. The correct model is not a daemon that occasionally dies; it is something that runs when it is allowed to.
ADR 0061 asks an init for start at boot and has a launcher supervise the host. Android grants neither half: nothing to ask, and nothing worth supervising, because the supervisor would be killed alongside what it supervises.
Decision
A host is either resident or episodic, and both are hosts.
| resident | episodic | |
|---|---|---|
| started by | an init, at boot | whatever the platform allows — an app's foreground service, a scheduled wake |
| supervised by | the launcher (ADR 0061) | nothing; the platform decides when it runs |
| the link | held open | opened while it runs |
| stopping | shutdown, or a failure | ordinary, and needs no explanation |
Being killed is not a failure to detect. It is disconnection, which ADR 0036 already made an ordinary situation rather than an exception — a node that is switched off, roaming, or behind a connection that has dropped has not become a lesser kind of thing. An episodic host is that, more often.
This needs no new mechanism, and that is the argument for it. The store is already authoritative
while disconnected. Reconcile already happens on start. The mesh already reports last heard
from rather than alarming on silence
(09-the-node-lifecycle.md). Every one of
those was decided for laptops that close, and an episodic host is the same case with a shorter
period.
What an episodic host does not have
- No launcher. There is nothing to supervise it and nothing for it to supervise. The platform starts it; the platform stops it.
- No rollback. ADR 0061's recovery reinstalls a previous package, and an episodic host has no package manager to reinstall from. A bad version is replaced the way the platform replaces applications.
- No bundle, and therefore no bootstrap. Every step of raising a substrate is a shape a partial host refuses, so a partial host can join a mesh and cannot be the first node. The android bundle says exactly that instead of being an empty placeholder.
What it still is
A node. It has an identity, it holds a store, it applies declarations, it reads back and reports. It is reachable in the inventory, it can be assigned work of the kinds it supports, and it is not a second class of thing in the model — ADR 0036 is explicit that reachability is state rather than class, and this is that rule doing the work it was written for.
Consequences
- The gap 0060 left is closed by narrowing what is required, not by building something. No Android daemon, no keep-alive service, no fighting the platform's process management — which would be a losing fight and a permanent source of bugs.
- The heartbeat matters more and means less. An episodic host reports when it runs, so last heard from on a phone is a much weaker signal than on a server. Anything reading that fact has to know which kind of host it is looking at, or a healthy phone reads as a dead node.
- A declaration may take a long time to land, because the node applies it only when the platform next runs it. Outstanding was already separated from failed (ADR 0058) and this makes that separation load-bearing rather than tidy.
- How an episodic host is actually started is still platform work and is not designed here. An APK with a foreground service, or Termux with its boot addon — both are real, both have costs, and choosing between them wants an actual device and an actual purpose for it.
- What an Android node is FOR remains unanswered, and it should be answered before the platform work is done. A device that can write files and run commands is not a workload host; it is a presence, or somewhere an agent runs. Building the start mechanism before deciding that would be building it for nobody.