---
title: Decision — hostd, the host service
description: One root service per host answers host information and carries out privileged host actions through closed, typed, reversible verbs, so agents can act autonomously without sudo.
---

# Decision — hostd, the host service

Status: accepted 2026-10-12.

## The decision

- **One service, `hostd`, runs as root on each host.** It has two jobs:
  information about the host (hardware, OS, the MixOS mode, network
  management state, devices, power) and privileged actions on the host.
  It is the host agent of the VT-native design: the only MixOS code on the
  host besides `mix`.
- **No generic exec.** No verb runs a caller-supplied command, script or
  shell string. Each verb does one job, checks its arguments against a
  schema and does the work natively (writes the file itself, calls
  networkd, resolved, logind or iwd over Varlink or D-Bus).
- **Written in Rust.** Mix is the client. A root process that interprets
  scripts is too much attack surface.
- **A verb that can't be reversed is not added.** Exception: the two desk
  power verbs, `desk.restart` and `desk.stop` (see Amendments).
- **Every new verb is reviewed on its own,** and the verb list stays short.

### Callers and transport

- **Local callers** use a Unix socket. Identity comes from the kernel
  (`SO_PEERCRED`) and the caller's cgroup, never from the request.
- **Reads are also served on the Bus,** like any other service's.
- **Writes from other nodes** are accepted only when signed by a controller
  key listed in that host's policy, over the target node, a nonce and an
  expiry. Mesh trust alone never authorises a write. A host with no listed
  controller accepts no remote writes. Enforcement is on the receiving
  host.
- **Ports below 1024 are not hostd's job.** A service that binds one gets
  `AmbientCapabilities=CAP_NET_BIND_SERVICE` in its own unit.

### Tiers

| Tier | Examples | Gate |
|---|---|---|
| read | host info, network state, devices | caller identity |
| reversible | Wi-Fi connect, link configuration, device bind, suspend | a standing grant, plus a dead-man check or a kept previous state |
| reversible, wide blast radius | firewall, host package upgrade, reboot | a grant, a second signature from an independent reviewer, and a rate limit |
| irreversible | — | no verb exists |

### Autonomy within limits set in advance

- **Standing grants,** not per-call prompts. A grant names the caller (from
  the kernel), the verbs, argument limits and an optional expiry. Only
  creating or widening a grant needs a person, confirmed through physical
  input that no Bus caller can synthesise.
- **Reversible by construction.** A verb applies its change, runs a health
  check, and reverts unless the check passes in time. It keeps the previous
  state (the previous file, or a filesystem snapshot) behind a receipt that
  `hostd.undo` restores.
- **Limits:** a rate limit per caller and verb, a circuit breaker after
  repeated rollbacks, and a `freeze` that drops hostd to reads only.
- **Audit:** one journal record per call.

### Files hostd owns

hostd writes only files it names itself (networkd files are
`/etc/systemd/network/50-hostd-*`) and never edits a file it didn't write.

## Why

- Agents need to change host configuration (network, WireGuard, power,
  devices) without `sudo` from a session, and without a person approving
  each call.
- MixOS core has no polkit. hostd is the mechanism and its policy in one
  small binary, with rules as data instead of scripts.
- Closed verbs make a stolen credential worth far less than a root shell.

## Consequences

- Session code never shells out to `sudo`; a missing host action is a new
  hostd verb.
- Write verbs arrive one reviewed verb at a time. The first is
  `net.link.apply`; the verbs, grants and refusals are documented in the
  [host service manual](../../hostd/README.md).
- **Root on the host** means uid 0 in hostd's own pid and user namespaces,
  proven through a pidfd. Root inside a container is never root on the
  host, with or without uid remapping.

## Amendments

### 2026-10-13: desk power verbs are an exception to "no irreversible verbs"

`desk.restart` and `desk.stop` restart or power off one desk machine. A
powered-off desk cannot be put back by hostd, so no undo can exist. The
desk must still be able to restart or stop itself, so the two verbs are
admitted as exceptions to the irreversibility rule. They are not a pattern
for other verbs.

Reversibility is replaced by these limits, which apply to both verbs:

- **One desk per grant.** The verb takes no machine name. The grant's
  `desks` limit must name exactly one valid machine, and that machine is the
  only one the verb can touch. A caller with no desk, or two, is refused.
- **The host is never powered.** The verbs run `machinectl reboot` or
  `machinectl poweroff` on the named machine only.
- **A standing grant, rate limited.** Nothing runs without a grant, and a
  grant carries a rate limit and an expiry.
- **Freeze applies.** `hostd.freeze` refuses both verbs.
- **Every call is recorded,** as a receipt and in the audit journal. `hostd.undo`
  refuses both verbs.

Deferred (Mark, 2026-10-13): the second signature from an independent reviewer
that the reboot tier requires is not built for `desk.*`. Security gates are
added once the desktop is ready for daily use, where they are needed. Until
then, the grant's rate limit and expiry are the only second check.
