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.
102 lines
5.5 KiB
Markdown
102 lines
5.5 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-08-28
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0060-the-host-is-built-per-operating-system.md
|
|
---
|
|
|
|
# 62. A host may be episodic, and being killed is ordinary
|
|
|
|
## Context
|
|
|
|
[ADR 0060](0060-the-host-is-built-per-operating-system.md) 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](0061-the-host-asks-an-init-for-start-and-restart.md) 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](0061-the-host-asks-an-init-for-start-and-restart.md)) | 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](0036-a-node-is-a-managed-machine.md) 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`](../03-DESIGN/01-to-be/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](0061-the-host-asks-an-init-for-start-and-restart.md)'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](0036-a-node-is-a-managed-machine.md) 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](0058-delivery-ends-in-a-declaration.md)) 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](0036-a-node-is-a-managed-machine.md) — disconnection as an ordinary situation,
|
|
which this is an instance of rather than an extension to.
|
|
- [ADR 0060](0060-the-host-is-built-per-operating-system.md) — partial hosts, and the gap this
|
|
closes.
|
|
- [ADR 0061](0061-the-host-asks-an-init-for-start-and-restart.md) — what a resident host has
|
|
that this one does not.
|