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.
1. The model: three layers
Section titled “1. The model: three layers”The provisioning design (its record is ../integrate/provisioning.md) structures configuration as three layers:
- Layer 1 — first-boot self-provisioning. From empty DATA/state the device seeds identity, hostname, secrets and defaults, offline, exactly once. Shipped; described in ../start/first-run.md.
- Layer 2 — local configuration channels. The authenticated 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. - 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/integrate/provisioning.md,mica-core:crates/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 to apid — over HTTP on 8080, or HTTPS on 8443 once enabled — through the
built-in UI at /_ui/ or the JSON API under /api/v1 (contract:
mica-core:crates/mica-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:crates/mica-apid/openapi.json,docs/reference/management.md
3. What is configurable today
Section titled “3. What is configurable today”The settings tree currently models, per subtree:
- hostname;
- network — interfaces by name, each with a declared kind:
physical,
vlan,bridge,wireguardorcan, rendered into systemd-networkd units, or ifupdown stanzas on OpenRC. Acanentry is a CAN controller’s bit rate, bus-off restart and CAN FD mode, with no addressing; a board may ship its CAN interfaces as defaults. A WireGuard tunnel’s private key is generated on the device and never enters the settings tree; rotation is an explicit API action; - dns — global DNS servers, which replace every interface’s own while switched on, and static host records, which the device resolves from its hosts file. There is no fallback DNS: with the global servers off, the device asks only the servers its interfaces were given, so a site whose router hands out no DNS server resolves nothing;
- wifi.client and wifi.ap — WiFi station and access-point roles,
driving wpa_supplicant and hostapd. The access point runs
off, while no uplink exists (provisioning) oralways, with WPA2, WPA2 and WPA3 side by side, or no key at all. On a product with WiFi, first boot turns it on with the station off, and its name and key are the ones on the device’s label until the device is claimed (first run section 2); radios are a board fact; - bluetooth — the switch, the trust list and
provisioning.autoWhenOffline, which opens Bluetooth provisioning when a claimed device loses its uplink (first run section 5). The adapter is neither discoverable nor pairable: pairing a device happens in a 180-second window opened from the console, with a numeric comparison confirmed there, and a fixed PIN is refused; - access.ssh — SSH enablement, port, listen addresses, authorized keys, and the policy around the transient root password (see ../security/overview.md);
- container — the switch gating the container engine and the declared Compose projects (../integrate/containers.md);
- mqtt — the local MQTT broker and application-data bridge (../integrate/applications.md);
- time — the time source and synchronisation policy (section 5.1);
- services.units — the user’s own systemd units or OpenRC scripts, kept on
DATA and declared here, also from
micad uniton the device (native applications section 10); - power actions and update actions — not settings, but reachable over the same authenticated surface.
Update settings are not in this tree. The update mode, 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.
updates.md is what each one does.
The authoritative list is the API contract, not this prose: what
mica-core:crates/mica-apid/openapi.json accepts is what the device supports.
status: shipped — evidence:
mica-core:crates/mica-apid/openapi.json,mica-core:crates/micad-settings,mica-core:docs/mica-core.md
WiFi presence is board-dependent: cx3576 and s905x5m carry WiFi and Bluetooth
in their full products; uefi-x64, mini-x64 and qemu-armhf have no radio.
status: board-dependent — evidence:
mica-build:boards/cx3576/board.env,mica-build:boards/s905x5m/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) are enumerated in the design record, and integrator files, scripts
and data belong on DATA (storage.md).
status: shipped — evidence:
docs/reference/access.md,docs/reference/ro-root.md
5. Deliberate limits, and what is still missing
Section titled “5. Deliberate limits, and what is still missing”5.1 Time
Section titled “5.1 Time”Two things about network time 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/reference/time.md,mica-core:crates/micad/src/time_status.rs
status: board-dependent — evidence:
mica-linux@mica/cx3576:arch/arm64/boot/dts/rockchip/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 (../start/first-run.md). It is the repeatable path for configuring a device that has never had a network. On a product with WiFi and Bluetooth, a phone can also send the device its WiFi network over Bluetooth LE, and claim it while it is unclaimed (first run section 5).
status: shipped — evidence:
docs/integrate/provisioning.md,mica-core:crates/micad/src/provisioning_doc.rs,mica-core:docs/ble-provisioning.md
There is no captive portal, no on-screen setup wizard and no serial
wizard, and each is an absence to plan around: the access point has its
transport and no portal behind it. 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 ../integrate/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