- Rust 100%
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). |
||
|---|---|---|
| crates | ||
| .gitignore | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
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:
- 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. - 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 anInstallConfig.
It is the machine-side counterpart of:
installer-ui— the operator-facing UI that callsmachined's gRPC service over the network (usesMachineServiceClient,ClaimRequest,SystemInfoRequest).installadm— builds the live image and stages USB sticks. It writes a defaultmachined.json(matchingMachineConfigshape) 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 bymachineconfig::parse_config). Triggers unattended install.- gRPC
InstallConfig.machineconfig— same KDL content as above, parsed withknus::parse("install_config", …)intoMachineConfig(seesrc/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 datasets —
rpool/ROOT/<be-name>mounted at/a. - Boot loader —
bootadm install-bootloader,bootadm update-archive. - Unpacked image —
gtar -xaf <layer>into/afor each OCI layer. - Progress stream —
InstallProgressmessages back over the gRPCInstallserver-streaming response. - Tracing logs —
/dev/msglogand 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 inimage/templates/files/machined-smf-service.xml. Started by/usr/lib/machined-wrapper(which mounts USB and execs/usr/lib/machined). Holds backsvc:/milestone/sysconfig,svc:/milestone/network,svc:/system/identity, andsvc:/network/routing-setupso it can run before they finalise.
Depended upon by machined
svc:/system/filesystem/rootsvc:/system/filesystem/minimalsvc:/network/loopbacksvc:/network/physicalsvc:/milestone/devicessvc:/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 inmachined-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/
-
✅
src/sysconfig.rs— implemented. TranslatesMachineConfig.sysconfiginto 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 underplatform/) so it's reachable on both illumos and mock builds. -
✅
src/platform/illumos/mod.rs::install_system— callscrate::sysconfig::write_fragment(Path::new("/a"), &mc.sysconfig)betweeninstall_imageandmake_be_bootable. Failures abort the install; "no sysconfig data" is logged at debug and continues. -
src/platform/mock/mod.rs— still referencesmc.sysconfig.{hostname,nameservers,interfaces}for logging. Fine as is, but should be updated to callcrate::sysconfig::write_fragmentinto a tempdir to keep mock and real aligned. (Open.) -
proto/machined.proto— no direct sysconfig refs, but theInstallConfigonly carriesmachineconfig. If we ever want to feed the runtime sysconfig a NoCloud user-data blob instead of KDL, the proto may want a paralleluserdatafield. (Optional, deferred.)
Outside machined/ but on the same install path
-
installadm/src/usb.rs:968-982—generate_default_configwrites a JSON blob with asysconfig: { hostname, nameservers, interfaces }key. This still matches the inlinedSysConfig, so it works, but thekind: "Dhcp4"enum value depends on themachineconfigcrate's address-kind enum. Action: verify enum tags still deserialize after the inline; otherwise switch to KDL viasample.kdl. -
✅
image/templates/files/machined-smf-service.xml:72-74— updated to depend onsvc:/system/sysconfig(the standalone service) instead of the legacysvc:/milestone/sysconfig. -
image/templates/files/sysconfig-release/— the live image still shipssysconfig.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 toimage/forger/files/lib/svc/manifest/system/sysconfig.xml. -
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. -
image/templates/include/sysconfig-release.json,sysconfig-release-bins.json,sysconfig-dev.json—ensure_filerecipes 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/sysconfigbinary plus/lib/svc/manifest/system/sysconfig.xmlplus default/etc/sysconfig.d/00-defaults.kdl. The forger image already has the correct pattern inimage/forger/files/lib/svc/manifest/system/sysconfig.xmlandimage/forger/files/etc/sysconfig.d/00-defaults.kdl— copy those. -
image/templates/cloudimage/ttya-openindiana-hipster*.json— these cloud-image build templatesincludethe 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
-
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. -
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
InstallgRPC handler insrc/main.rs:97swallows install errors withErr(_) => {}— failures are not propagated back to the client beyond the streamedreport_install_errormessages, and the spawned task ends silently. src/main.rs:67—ClaimSecret::ClaimPayloadarm istodo!(). WireGuard / encrypted-claim path is unimplemented.MachinedConfig.wireguardandMachinedConfig.server(CommandServer) are wired into the config struct but never used.make_be_bootablereturns apool_namebut never uses the unused-by-designbootadm install-bootloader -Pparameter consistently with the rest of the install path; behaviour on multi-pool installs is unverified.- The
tokioruntime iscurrent_threadwhiletokio::spawnis used for the install task — install runs cooperatively on the same thread as the gRPC server, so a slowgtarstep blocks new RPCs. main.rs:144-148— opens/dev/msglogforappendwithcreate=false; this hard-fails the daemon outside the live ramdisk environment, making local debugging awkward.installer-uidoesn'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:
machined/src/sysconfig.rs—write_fragment(&Path, &SysConfig)emits a KDL fragment to<root>/etc/sysconfig.d/50-installer.kdl.machined/src/platform/illumos/mod.rs— callscrate::sysconfig::write_fragment(Path::new("/a"), &mc.sysconfig)betweeninstall_imageandmake_be_bootable.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.image/templates/files/machined-smf-service.xml— depends onsvc:/system/sysconfig(the standalone service) instead of the legacysvc:/milestone/sysconfig.image/templates/files/sysconfig-release/— delete the directory (old gRPC daemon manifests + plugins + toml). Replace with the new-sysconfig manifest fromimage/forger/files/lib/svc/manifest/system/sysconfig.xmland defaults fromimage/forger/files/etc/sysconfig.d/00-defaults.kdl.image/templates/files/sysconfig-dev/— delete; replace with the new sysconfig binary placed under/usr/lib/sysconfig/sysconfig.image/templates/include/sysconfig-release.json— rewrite as a single-binary recipe (binary + manifest + defaults).image/templates/include/sysconfig-release-bins.json— delete or merge intosysconfig-release.json.image/templates/include/sysconfig-dev.json— rewrite for new binary.image/templates/cloudimage/ttya-openindiana-hipster.jsonand…-dev.json— re-point includes at the new sysconfig include names.installadm/src/usb.rs:947-990— verify the JSON written to USB still deserialises cleanly intoMachineConfigafter the sysconfig inlining. Consider switching the default to KDL (sample.kdlshape) since that is what gRPCInstallConfigcarries.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.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.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-treesysconfig/,sysconfig-cli/,sysconfig-plugins/,sysconfig-provisioning/.
Items 1–4 are the minimum to make machined functionally apply sysconfig. Items 5–10 are required so the live installer image stops shipping (and trying to start) the deleted old daemon. Items 11–14 are hygiene.