Skip to content

Configuration

Mica OS configuration is a single typed settings tree, owned by the management daemon (micad), stored as JSON documents under /mica/config/ on the data partition, and applied by reconcilers that drive the underlying system services. Every supported way to change the device converges on the typed schema. Online changes use the authenticated API; offline files are validated at startup. This page covers the model, the channels, what is configurable today, and — just as important on an immutable system — what is not.

The provisioning design (its record is ../design/provisioning.md) structures configuration as three layers:

  1. Layer 1 — first-boot self-provisioning. From empty DATA/state the device seeds identity, hostname, secrets and defaults, offline, exactly once. Shipped; described in first-run.md.
  2. Layer 2 — local configuration channels. The authenticated HTTPS API and built-in UI support live changes. A validated provisioning document on the boot medium or removable media configures an unclaimed device offline. An integrator can also prepare the documented /mica/config/ files while the device is stopped; startup validates them before applying them.
  3. Layer 3 — build-time defaults. The image manifest supplies update and fleet defaults and immutable trust material. Operator documents override the supported configuration keys; they cannot replace the signing keys. This is not a general factory credential-injection toolchain.

status: shipped — evidence: docs/design/provisioning.md, mica-core:micad/

2. Online configuration: the API and the built-in UI

Section titled “2. Online configuration: the API and the built-in UI”

Online configuration reads and writes go over HTTPS to apid — through the built-in UI at /_ui/ or the JSON API under /api/v1 (contract: mica-core:apid/openapi.json, see api.md). apid owns browser sessions and API support state; it forwards settings changes to micad over the local system bus, where the write is validated against the typed schema. A rejected write leaves the tree untouched, and a settings write returns a task you can observe until the matching reconciler has applied it.

Where settings are stored, and what that means for resets. System configuration — hostname, network, WiFi, SSH, MQTT, time, the container switch and the update settings — is written as one JSON document per subsystem under /mica/config/ on the data partition. What the device mints or observes about itself stays on DATA/state: the device identity, the administrator credential, API tokens. Both survive a reboot and an A/B update, because an update writes only SYSTEM deployments.

The split is the reset boundary rather than a storage detail: a configuration reset returns everything under /mica/config/ to the values the image was built with and keeps the administrator credential, which is why that tier is a settings action and not a lockout. Read recovery.md Step 3 before running one — the update server address goes back too, and where the image names no server that leaves the device with none.

status: shipped — evidence: mica-core:apid/openapi.json, docs/design/micad.md

The settings tree currently models, per subtree:

  • hostname;
  • network — wired interfaces by name, each with a declared kind: physical, vlan, bridge or wireguard, rendered into systemd-networkd units. A WireGuard tunnel’s private key is generated on the device and never enters the settings tree; rotation is an explicit API action;
  • wifi.client and wifi.ap — WiFi station and access-point roles, driving wpa_supplicant and hostapd (the AP exists for provisioning and product use; radios are a board fact);
  • access.ssh — SSH enablement, port, listen addresses, authorized keys, and the policy around the transient root password (see security.md);
  • container.enabled — the single switch gating the container runtime (applications.md);
  • mqtt — the local MQTT broker and application-data bridge (applications.md);
  • time — the time source and synchronisation policy (section 5.1);
  • power actions and update actions — not settings, but reachable over the same authenticated surface.

Update settings are not in this tree. The update mode, channel, server address, maintenance windows and network mode live in their own document, /mica/config/updates.json, beside the ones above rather than inside the settings schema. They have their own route — POST /api/v1/update/config, which takes only the keys you are changing — and the built-in UI’s automatic-updates panel drives it. update-rollback.md is what each one does.

The authoritative list is the API contract, not this prose: what mica-core:apid/openapi.json accepts is what the device supports.

status: shipped — evidence: mica-core:apid/openapi.json, mica-core:micad-settings/

WiFi presence is board-dependent: the cx3576 carries WiFi and Bluetooth; the x64 QEMU baseline has no radio.

status: board-dependent — evidence: mica-boards:boards/cx3576/board.env

4. An unmodelled setting is an unsupported setting

Section titled “4. An unmodelled setting is an unsupported setting”

The root filesystem is a verity-protected read-only squashfs. There is no /etc overlay and there will not be one: an edit under /etc either fails outright or lands in memory and vanishes on reboot. Anything that must persist must be modelled in the settings tree and exposed by the API — if the API cannot set it, the appliance does not support persisting it. The deliberate exceptions (paths bound onto DATA, such as /etc/ssh or the Quadlet directory) are enumerated in the design record, and integrator files, scripts and data belong on DATA (storage.md).

status: shipped — evidence: docs/design/access.md, docs/design/ro-root.md

5. Deliberate limits, and what is still missing

Section titled “5. Deliberate limits, and what is still missing”

Network time is not a gap any more, but two things about it are deliberate limits rather than omissions.

systemd-timesyncd is installed and statically enabled, so it always runs: there is no enable, disable or pause control — not in the settings tree, not in the API, not in the UI. Two settings exist and only two, time.ntp.servers (a validated list; empty means the image’s fallback pool) and time.timezone. GET /api/v1/time/status reports whether the kernel bounds the clock’s error (synchronized) or a server is merely answering (polling), which server that is, and whether the last correction was a step or a slew.

The machine’s time is UTC, always, and the timezone is presentation only. time.timezone is never applied to /etc/localtime — the image bakes no zone and the build refuses one — because that symlink is read by every consumer of UTC, journald included. The zone is published as a runtime value the UI formats with, and nothing else.

Below network time sits a clock floor, so a device with no RTC or a dead RTC battery still boots no earlier than the last minute it was known to be running: the saved clock lives on DATA/state and survives reboots and A/B updates. PTP, NTS and configurable polling periods are out of scope.

Unproven on hardware: the cx3576’s device tree declares an RTC, and neither its driver nor its backup power has been validated on a bench board. Nothing on this page depends on the RTC being there — that is what the saved floor is for — but a device that keeps time across a long power-off has not been demonstrated.

status: shipped — evidence: docs/design/time.md, mica-core:micad/src/time_status.rs

status: board-dependent — evidence: mica-boards:boards/cx3576/kernel/dts/rk3576-cx3576z.dts

5.2 Offline provisioning channels and factory injection

Section titled “5.2 Offline provisioning channels and factory injection”

A versioned, validated provisioning document now exists and is applied from a medium before anything is listening on the network: one TOML file at a fixed name, whole-document validation, applied once and recorded, over two offline transports (first-run.md). It is the repeatable path for configuring a device that has never had a network.

status: shipped — evidence: docs/design/provisioning.md, mica-core:micad/src/provisioning_doc.rs

Three of the five channels the design lists do not exist, and each is an absence to plan around rather than work in progress: the AP captive portal has its transport and not the portal, the HDMI local setup wizard has a browser client and no kiosk or input chain to run it on a device’s own screen, and the serial console wizard was never built. Nor does the shipped removable-media path verify a signature or import on hotplug — media are consulted once, at boot. Factory injection is absent too: versioned inputs, verification at injection and a per-device record are described in manufacturing.md and have no tooling. Offline preparation of the documented /mica/config/ files requires a stopped device; live changes use the authenticated API over an existing network.

status: unsupported