Files
hq/02-DECISIONS/0062-a-host-may-be-episodic.md
T
jschoubben ba0d01788e 0062: a host may be episodic; 0060's Android gap closed
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.
2026-08-28 01:24:07 +02:00

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.

References

  • ADR 0036 — disconnection as an ordinary situation, which this is an instance of rather than an extension to.
  • ADR 0060 — partial hosts, and the gap this closes.
  • ADR 0061 — what a resident host has that this one does not.