No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Till Wegmueller e58e995d43 fix(users): drop non-existent supplementary groups instead of aborting
useradd/usermod -G treats a missing group as a hard error and aborts
the whole user creation. Cloud-config carries cross-distro group names
(Linux sudo/docker) that need not exist on illumos, so a job user with
groups: [sudo, docker] was never created and the orchestrator could not
SSH in. Filter -G to the groups present in /etc/group and warn about the
rest; sudo access comes from the separate sudoers.d drop-in anyway.
2026-07-12 22:05:12 +02:00
crates fix(users): drop non-existent supplementary groups instead of aborting 2026-07-12 22:05:12 +02:00
docs runners/source: HTTP, OCI, and archive backends 2026-05-05 23:18:38 +02:00
examples Initial sysconfig: typed KDL config tool for illumos cloud images 2026-04-28 14:51:11 +02:00
service-configs/smf Initial sysconfig: typed KDL config tool for illumos cloud images 2026-04-28 14:51:11 +02:00
.gitignore Initial sysconfig: typed KDL config tool for illumos cloud images 2026-04-28 14:51:11 +02:00
Cargo.lock feat(cloud-init): apply write_files entries at provision time 2026-07-11 22:05:35 +02:00
Cargo.toml runners/source: HTTP, OCI, and archive backends 2026-05-05 23:18:38 +02:00
LICENSE Initial commit 2026-04-28 12:40:29 +00:00
README.md Initial sysconfig: typed KDL config tool for illumos cloud images 2026-04-28 14:51:11 +02:00

sysconfig

A typed, declarative system configuration tool for illumos cloud images. A simpler, faster, type-safe replacement for cloud-init.

Why sysconfig?

cloud-init is a 60k+ line Python project that boots ~10 SMF services and takes 30+ seconds on a small VM. It has been the de facto standard for cloud provisioning, but on illumos it's a poor fit:

  • Slow: serial Python startup + many SMF services
  • Untyped: YAML with implicit schema, errors only at runtime
  • Linux-centric: many features assume systemd, Debian, NetworkManager
  • Three daemons: cloud-init-local, cloud-init, cloud-config, cloud-final

sysconfig is a single Rust binary, one transient SMF service, KDL config:

cloud-init sysconfig
Daemons 4 SMF services 1 transient
Config format YAML typed KDL
Lines of code ~60,000 (Python) ~3,500 (Rust)
Boot overhead 30+ seconds <1 second
Self-assembly merge order is implicit /etc/sysconfig.d/*.kdl lex order

Quick start

The fastest way to try sysconfig is the prebuilt OmniOS image:

# Pull and run with vmctl (https://code.aopc.cloud/CloudNebulaProject/vm-manager)
vmctl create --name myvm \
    --image-url oci://code.aopc.cloud/cloudnebulaproject/omnios-sysconfig:latest \
    --vcpus 2 --memory 2048 \
    --start

The VM boots, sysconfig detects the NoCloud seed ISO that vmctl created, applies hostname/SSH keys/users from your cloud-init user-data, and exits — no daemons left running.

Default root password (until you provide one): test123.

How it works

boot
  ↓
SMF starts svc:/system/sysconfig:default  (transient)
  ↓
sysconfig provision --source auto
  ├── detect cloud env (NoCloud, EC2, GCP, …)
  ├── fetch metadata (mount CIDATA, HTTP, mdata-get, …)
  ├── write /etc/sysconfig.d/10-cloud.kdl
  └── invoke `sysconfig apply`
        ├── load all /etc/sysconfig.d/*.kdl in lex order
        ├── merge into a typed SysConfig
        ├── validate
        └── apply via modules: hostname → DNS → interfaces → users
  ↓
exit 0  (service goes online, then idle)

The merged config is the source of truth — everything else is just a way to populate it. Drop your own 50-local.kdl next to the cloud fragment and your overrides win.

Configuration

sysconfig {
    hostname "web01"
    timezone "Europe/Zurich"

    nameserver "1.1.1.1"
    nameserver "1.0.0.1"
    search-domain "example.com"

    interface "net0" {
        address name="v4" kind="dhcp4"
    }

    interface "net1" selector="mac:52:54:00:11:22:33" {
        address name="v4" kind="static" "10.0.0.5/24"
        address name="v6" kind="addrconf"
    }

    user "admin" {
        shell "/usr/bin/bash"
        groups "wheel" "staff"
        sudo "unrestricted"
        ssh-key "ssh-ed25519 AAAA…"
    }
}

See docs/configuration.md for the full schema.

Self-assembly via fragments

/etc/sysconfig.d/ is read in lexicographic order. Each fragment is a complete sysconfig { … } block. Later fragments override earlier ones for scalars (hostname, timezone) and append-with-dedup for lists (nameservers, users, interfaces).

Recommended layout:

File Source Purpose
00-defaults.kdl image shipped baseline (DHCP, UTC, sane DNS)
10-cloud.kdl sysconfig written by sysconfig provision
50-local.kdl admin local overrides
90-override.kdl admin last-word overrides

This is the "drop-in" pattern, the same as systemd and cron.d.

Commands

sysconfig apply [--dry-run] [--config-dir /etc/sysconfig.d]
sysconfig provision [--source auto|nocloud|ec2|] [--dry-run]
sysconfig diff [--config-dir /etc/sysconfig.d]
sysconfig validate [PATH...]
  • apply — merge fragments and configure the system
  • provision — detect cloud, fetch metadata, write fragment, then apply
  • diff — show what apply would do (alias for apply --dry-run)
  • validate — check syntax and semantics of fragment files

Status

v0.1.0 — early access. Works end-to-end on OmniOS bloody. Tested with NoCloud (vmctl/cloud-localds). EC2/GCP/Azure providers are scaffolded but not yet wired into release builds.

Module illumos Linux FreeBSD
hostname (untested)
DNS (/etc/resolv.conf) (untested)
interfaces (ipadm) gated
users (useradd/usermod) (untested)
SSH keys (untested)
sudo (/etc/sudoers.d/) (untested)
NoCloud (CIDATA mount) (untested)
cloud-init user-data parser

Building

cargo build --release

Cross-compiling to illumos from Linux requires the illumos C toolchain (for ring/reqwest linkage). The simplest path is to build natively inside an illumos VM. See docs/building.md.

Repo layout

crates/
  sysconfig-schema/     typed config + KDL parsing + fragment merging
  sysconfig-modules/    platform actions (hostname, DNS, ifaces, users)
  sysconfig-providers/  cloud metadata fetchers (NoCloud today)
  sysconfig/            CLI binary
service-configs/smf/    SMF manifest (single transient service)
examples/               example KDL fragments
docs/                   user and developer documentation
tests/                  integration tests

Documentation

License

MPL-2.0. See LICENSE.