No description
Find a file
Till Wegmueller 9ad30f3016 deps: pull ociclient from its own repo, drop vendored copy
ociclient now lives at its own home:
  https://code.aopc.cloud/CloudNebulaProject/oci-client

Removed crates/ociclient/ and switched the path dep in
crates/machined/Cargo.toml to a git dep on the new repo. The
workspace now contains only machined and machineconfig (the latter
remains canonical here).
2026-04-28 16:47:34 +02:00
crates deps: pull ociclient from its own repo, drop vendored copy 2026-04-28 16:47:34 +02:00
.gitignore Initial machined: install-time daemon for the illumos installer 2026-04-28 16:05:59 +02:00
Cargo.toml deps: pull ociclient from its own repo, drop vendored copy 2026-04-28 16:47:34 +02:00
LICENSE Initial machined: install-time daemon for the illumos installer 2026-04-28 16:05:59 +02:00
README.md deps: pull ociclient from its own repo, drop vendored copy 2026-04-28 16:47:34 +02:00

machined

machined is the install-time daemon that runs inside the live installer image on a machine being provisioned. It is the on-target component of the illumos installer toolchain: it claims the machine, fetches an OS image from an OCI registry, lays down ZFS pools and a boot environment, unpacks the image into /a, and makes the resulting boot environment bootable.

This repository contains machined and the canonical copy of machineconfig (the parser for the install-time KDL spec) as a Cargo workspace:

crates/
├── machined/        the install-time daemon (this README)
└── machineconfig/   parser for the install KDL spec — depended on
                    via git from installer-ui and the installer monorepo

ociclient (the OCI registry client used to fetch OS images) lives in its own repo at https://code.aopc.cloud/CloudNebulaProject/oci-client and is consumed via a git dependency.

machined is consumed on-target as /usr/lib/machined, started by SMF svc:/system/installer/machined:default via the wrapper /usr/lib/machined-wrapper (manifest in the installer repo at image/templates/files/machined-smf-service.xml).


Overview

machined is the runtime that turns a MachineConfig (KDL) into a fully installed illumos system. It runs in two modes:

  1. Headless / unattended — if a machine config is found on a USB stick at /usb/machined.{kdl,json,toml,yaml,yml}, the daemon runs the install directly and exits.
  2. Attended / network — otherwise it starts a gRPC server on 0.0.0.0:50051, prints all interface addresses and a one-time claim password to /dev/msglog, and waits for an installer UI / orchestrator (installer-ui, instcomd) to claim it and stream an InstallConfig.

It is the machine-side counterpart of:

  • installer-ui — the operator-facing UI that calls machined's gRPC service over the network (uses MachineServiceClient, ClaimRequest, SystemInfoRequest).
  • installadm — builds the live image and stages USB sticks. It writes a default machined.json (matching MachineConfig shape) onto the USB for unattended installs.
  • instcomd — central orchestrator/inventory server; stores per-machine config blobs that get pushed down.

Architecture

                       ┌──────────────────────────────┐
                       │ installer-ui / instcomd      │
                       │ (gRPC client, off-target)    │
                       └──────────────┬───────────────┘
                                      │ tonic gRPC, port 50051
                                      │ Claim / Install / GetSystemInfo
                                      ▼
   /usb/machined.kdl ──────►  ┌──────────────────────┐
   (unattended path)          │ machined  (Svc)      │
                              │  ─ JWT claim auth    │
                              │  ─ knus parse KDL    │
                              │  ─ sysinfo (smbios)  │
                              └──────────┬───────────┘
                                         │
                                         ▼
                              ┌──────────────────────┐
                              │ platform::illumos    │
                              │  ─ zpool::create_*   │  zpool/zfs/beadm/bootadm
                              │  ─ image::fetch/install│ ociclient → gtar -xaf -C /a
                              │  ─ sysconfig::write_fragment │ → /a/etc/sysconfig.d/50-installer.kdl
                              │  ─ make_be_bootable  │
                              └──────────────────────┘

Source layout

File Role
src/main.rs Entry point. Tracing setup, USB-config detection, gRPC server, claim/install/get_system_info handlers.
src/machined.rs Tonic-generated gRPC types (built by build.rs into src/).
src/config.rs MachinedConfig (TOML/JSON via config crate) — daemon's own config (claim password, listen addr, OCI registry, optional WireGuard, claim key).
src/error.rs InstallationError (miette diagnostic).
src/util.rs Helpers building InstallProgress messages for the gRPC stream.
src/process.rs run_capture_stdout helper with locale-stripped env.
src/devprop.rs Wrapper around /sbin/devprop (boot-time kernel device properties).
src/sysinfo.rs Implements GetSystemInfo — runs diskinfo, dladm, smbios and fills the gRPC structs.
src/platform/mod.rs Conditional #[cfg(target_os="illumos")] switch between real and mock platform impls.
src/platform/illumos/mod.rs install_system orchestrator: pool → BE → image → bootable.
src/platform/illumos/zpool.rs zpool create, zfs create, beadm-style BE dataset setup, mount.
src/platform/illumos/image.rs OCI registry pull (ociclient), platform-aware manifest selection, gtar -xaf into /a.
src/sysconfig.rs Renders MachineConfig.sysconfig as a KDL fragment and writes it to /a/etc/sysconfig.d/50-installer.kdl for the standalone runtime sysconfig to apply at first boot. Pure logic + file I/O — platform-agnostic.
src/platform/mock/mod.rs Non-illumos build target — only logs what would happen. References mc.sysconfig.{hostname,nameservers,interfaces}.
proto/machined.proto gRPC service: Claim, Install (server-streaming progress), GetSystemInfo.
build.rs tonic-build compiles proto/machined.proto into src/machined.rs.
Vagrantfile Local VM for development.
testing.http Hand-test requests (JetBrains http-client).

Inputs / outputs

Inputs

  • /etc/machined.{toml,json,…} — baked into the live image. Daemon-level defaults (claim key seed, listen address, default OCI registry).
  • /usb/machined.{toml,json,…} — same shape, supplied via USB stick at install time, overrides /etc/.
  • /usb/machined.{kdl,json,toml,yaml,yml}MachineConfig (different shape! parsed by machineconfig::parse_config). Triggers unattended install.
  • gRPC InstallConfig.machineconfig — same KDL content as above, parsed with knus::parse("install_config", …) into MachineConfig (see src/main.rs:91).
  • /dev/msglog — write target for tracing output.
  • OCI registry (default aopc.cloud) — image source, fetched into /var/tmp/<image-name>/.

Outputs

  • ZFS pools and datasetsrpool/ROOT/<be-name> mounted at /a.
  • Boot loaderbootadm install-bootloader, bootadm update-archive.
  • Unpacked imagegtar -xaf <layer> into /a for each OCI layer.
  • Progress streamInstallProgress messages back over the gRPC Install server-streaming response.
  • Tracing logs/dev/msglog and stdout.

Sysconfig hand-off (since v0.1)

MachineConfig carries a sysconfig {} block (hostname, nameservers, interfaces, addresses). After install_image finishes laying down the rootfs at /a, machined renders this block as a KDL fragment and writes it to /a/etc/sysconfig.d/50-installer.kdl. On first boot of the installed BE, the standalone sysconfig service merges this fragment with image-shipped defaults (00-defaults.kdl) and any cloud-metadata fragment it discovers (10-cloud.kdl), then applies the merged config (hostname, DNS, interfaces, users).


SMF services

Provided by machined

  • svc:/system/installer/machined:default — defined in image/templates/files/machined-smf-service.xml. Started by /usr/lib/machined-wrapper (which mounts USB and execs /usr/lib/machined). Holds back svc:/milestone/sysconfig, svc:/milestone/network, svc:/system/identity, and svc:/network/routing-setup so it can run before they finalise.

Depended upon by machined

  • svc:/system/filesystem/root
  • svc:/system/filesystem/minimal
  • svc:/network/loopback
  • svc:/network/physical
  • svc:/milestone/devices
  • svc:/network/start-dhcp

Image-template includes that ship machined

  • image/templates/include/machined-dev.json — installs the binary, the wrapper, and the SMF manifest into the live image.
  • image/templates/installer/generic-ttya-ufs.json — pulls in machined-dev.

Dependencies (Cargo.toml)

Crate Why
miette (fancy) User-facing diagnostics on the daemon's startup path.
thiserror InstallationError enum.
ociclient (git, oci-client) Pull OS image from OCI registry.
machineconfig (path ../machineconfig) Parse the install KDL. Inlines SysConfig since the in-tree sysconfig was removed.
knus KDL parser used directly in main.rs for the gRPC-streamed config.
tokio (rt-multi-thread, macros) Async runtime — note main uses flavor = "current_thread" though.
tonic (zstd) + prost gRPC server + zstd compression.
tracing / tracing-subscriber Structured logging to /dev/msglog and stdout.
config Layered TOML/JSON loader for MachinedConfig.
serde (derive) Config + JSON.
nix (net) getifaddrs to print interfaces at startup.
passwords Random claim password generator.
jwt-simple (pure-rust) Mints / verifies the claim token (HS256).
base64 Decode the optional pre-seeded claim_key.private_key.
chrono Timestamped boot-environment names.
uuid (v4) org.opensolaris.libbe:uuid dataset property.
tonic-build (build dep) Compile .proto.

Spec / manifest / template files

Path What it is
machined/proto/machined.proto gRPC service definition.
machined/build.rs Compiles the proto.
machined/Cargo.toml Crate manifest.
machined/Vagrantfile Dev VM config.
image/templates/files/machined-smf-service.xml SMF manifest for the installed daemon.
image/templates/files/machined-wrapper.sh SMF start method; mounts USB, copies override binary if present, execs /usr/lib/machined.
image/templates/include/machined-dev.json Build-time recipe placing files in the image.
image/templates/installer/generic-ttya-ufs.json Top-level installer image template that includes machined-dev.

Sysconfig integration points (CRITICAL)

The old in-tree sysconfig (a gRPC daemon with state-manager + plugins, sockets at /var/run/sysconfig.sock, services like svc:/system/installer/sysconfig, svc:/system/sysconfig/illumos-base, svc:/system/sysconfig/provisioning) has been removed. The new sysconfig is a single binary at https://code.aopc.cloud/CloudNebulaProject/sysconfig that reads KDL fragments out of /etc/sysconfig.d/ and supports NoCloud cloud-init metadata directly. Below are every place machined and its surrounding spec files still touch the old design, with proposed fixes.

Inside machined/

  1. src/sysconfig.rsimplemented. Translates MachineConfig.sysconfig into a KDL fragment and writes it to /a/etc/sysconfig.d/50-installer.kdl. The fragment is consumed by the standalone sysconfig SMF service on first boot of the installed BE. Pure logic + file I/O, lives at the crate root (not under platform/) so it's reachable on both illumos and mock builds.

  2. src/platform/illumos/mod.rs::install_system — calls crate::sysconfig::write_fragment(Path::new("/a"), &mc.sysconfig) between install_image and make_be_bootable. Failures abort the install; "no sysconfig data" is logged at debug and continues.

  3. src/platform/mock/mod.rs — still references mc.sysconfig.{hostname,nameservers,interfaces} for logging. Fine as is, but should be updated to call crate::sysconfig::write_fragment into a tempdir to keep mock and real aligned. (Open.)

  4. proto/machined.proto — no direct sysconfig refs, but the InstallConfig only carries machineconfig. If we ever want to feed the runtime sysconfig a NoCloud user-data blob instead of KDL, the proto may want a parallel userdata field. (Optional, deferred.)

Outside machined/ but on the same install path

  1. installadm/src/usb.rs:968-982generate_default_config writes a JSON blob with a sysconfig: { hostname, nameservers, interfaces } key. This still matches the inlined SysConfig, so it works, but the kind: "Dhcp4" enum value depends on the machineconfig crate's address-kind enum. Action: verify enum tags still deserialize after the inline; otherwise switch to KDL via sample.kdl.

  2. image/templates/files/machined-smf-service.xml:72-74 — updated to depend on svc:/system/sysconfig (the standalone service) instead of the legacy svc:/milestone/sysconfig.

  3. image/templates/files/sysconfig-release/ — the live image still ships sysconfig.xml, sysconfig-illumos-base.xml, sysconfig-provision.xml, sysconfig.toml. These are the OLD gRPC-daemon manifests. Action: delete this entire directory; replace with a single new-sysconfig manifest equivalent to image/forger/files/lib/svc/manifest/system/sysconfig.xml.

  4. image/templates/files/sysconfig-dev/ — same problem in dev image variant: sysconfig-illumos-base-plugin.xml, sysconfig-provisioning.xml, sysconfig-smf-service-dev.xml, sysconfig.toml, dev-test.kdl, dev-9p-mount.xml. Action: delete and replace with a single dev manifest of the new sysconfig binary.

  5. image/templates/include/sysconfig-release.json, sysconfig-release-bins.json, sysconfig-dev.jsonensure_file recipes that place the old daemon binaries (sysconfig, sysconfig-cli, sysconfig-plugins/*, sysconfig-provisioning) into the live image. Action: rewrite to install the new single /usr/lib/sysconfig/sysconfig binary plus /lib/svc/manifest/system/sysconfig.xml plus default /etc/sysconfig.d/00-defaults.kdl. The forger image already has the correct pattern in image/forger/files/lib/svc/manifest/system/sysconfig.xml and image/forger/files/etc/sysconfig.d/00-defaults.kdl — copy those.

  6. image/templates/cloudimage/ttya-openindiana-hipster*.json — these cloud-image build templates include the now-stale sysconfig include files. Action: repoint at the new include, or drop entirely if the new sysconfig is shipped as a normal package.

Documentation

  1. installer-docs/src/sysconfig/{overview,format,examples,plugins}.md — describe the OLD gRPC plugin architecture and /var/run/sysconfig.sock. Action: rewrite to point at the new repo and the /etc/sysconfig.d/ fragment model.

  2. DEVELOPMENT_GUIDE.md, DEV_CLOUD_IMAGE.md, DEV_SETUP_SUMMARY.md, README_DEV_SETUP.md, ADVANCED_PROVISIONING_SUMMARY.md, UNIFIED_PROVISIONING.md, TOOLING_MISE.md, docs/setup.md, service-configs/install.sh, service-configs/README.md, service-configs/smf/sysconfig*.xml — all reference the in-tree sysconfig that no longer exists. Action: update or delete.


Known gaps

  • The Install gRPC handler in src/main.rs:97 swallows install errors with Err(_) => {} — failures are not propagated back to the client beyond the streamed report_install_error messages, and the spawned task ends silently.
  • src/main.rs:67ClaimSecret::ClaimPayload arm is todo!(). WireGuard / encrypted-claim path is unimplemented.
  • MachinedConfig.wireguard and MachinedConfig.server (CommandServer) are wired into the config struct but never used.
  • make_be_bootable returns a pool_name but never uses the unused-by-design bootadm install-bootloader -P parameter consistently with the rest of the install path; behaviour on multi-pool installs is unverified.
  • The tokio runtime is current_thread while tokio::spawn is used for the install task — install runs cooperatively on the same thread as the gRPC server, so a slow gtar step blocks new RPCs.
  • main.rs:144-148 — opens /dev/msglog for append with create=false; this hard-fails the daemon outside the live ramdisk environment, making local debugging awkward.
  • installer-ui doesn't use Install yet (grep shows it only uses Claim and GetSystemInfo) — confirm whether end-to-end install over gRPC actually works.

Where specs need updates — checklist

Concrete files to edit, in priority order. Items 1, 2, and 4 were closed when machined was extracted into its own repo; the remaining items live in the installer monorepo:

  1. machined/src/sysconfig.rswrite_fragment(&Path, &SysConfig) emits a KDL fragment to <root>/etc/sysconfig.d/50-installer.kdl.
  2. machined/src/platform/illumos/mod.rs — calls crate::sysconfig::write_fragment(Path::new("/a"), &mc.sysconfig) between install_image and make_be_bootable.
  3. machined/src/platform/mock/mod.rs — replace the per-field "Would set" log lines with a single "Would write /a/etc/sysconfig.d/50-installer.kdl" debug to mirror the real path.
  4. image/templates/files/machined-smf-service.xml — depends on svc:/system/sysconfig (the standalone service) instead of the legacy svc:/milestone/sysconfig.
  5. image/templates/files/sysconfig-release/ — delete the directory (old gRPC daemon manifests + plugins + toml). Replace with the new-sysconfig manifest from image/forger/files/lib/svc/manifest/system/sysconfig.xml and defaults from image/forger/files/etc/sysconfig.d/00-defaults.kdl.
  6. image/templates/files/sysconfig-dev/ — delete; replace with the new sysconfig binary placed under /usr/lib/sysconfig/sysconfig.
  7. image/templates/include/sysconfig-release.json — rewrite as a single-binary recipe (binary + manifest + defaults).
  8. image/templates/include/sysconfig-release-bins.json — delete or merge into sysconfig-release.json.
  9. image/templates/include/sysconfig-dev.json — rewrite for new binary.
  10. image/templates/cloudimage/ttya-openindiana-hipster.json and …-dev.json — re-point includes at the new sysconfig include names.
  11. installadm/src/usb.rs:947-990 — verify the JSON written to USB still deserialises cleanly into MachineConfig after the sysconfig inlining. Consider switching the default to KDL (sample.kdl shape) since that is what gRPC InstallConfig carries.
  12. installer-docs/src/sysconfig/{overview,format,examples,plugins}.md — rewrite for the new repo / new fragment model, or replace with a single page that links to the upstream sysconfig README.
  13. service-configs/smf/sysconfig.xml, service-configs/smf/sysconfig-illumos-base.xml, service-configs/smf/sysconfig-provision.xml, service-configs/install.sh, service-configs/README.md — old gRPC-daemon installer; remove or rewrite.
  14. DEVELOPMENT_GUIDE.md, DEV_CLOUD_IMAGE.md, DEV_SETUP_SUMMARY.md, README_DEV_SETUP.md, ADVANCED_PROVISIONING_SUMMARY.md, UNIFIED_PROVISIONING.md, TOOLING_MISE.md, docs/setup.md — purge references to in-tree sysconfig/, sysconfig-cli/, sysconfig-plugins/, sysconfig-provisioning/.

Items 14 are the minimum to make machined functionally apply sysconfig. Items 510 are required so the live installer image stops shipping (and trying to start) the deleted old daemon. Items 1114 are hygiene.