diff --git a/bin/omarchy-iso-test b/bin/omarchy-iso-test index 80cc7f5..7292632 100755 --- a/bin/omarchy-iso-test +++ b/bin/omarchy-iso-test @@ -14,8 +14,8 @@ # Usage: omarchy-iso-test [release/omarchy.iso] [options] # # --encrypt Drive the encrypted install flow (default: unencrypted) -# --oem Drive the OEM install flow: no user during install, the -# first boot runs omarchy-oem-setup and creates it there +# --provision Drive the deferred-provisioning install flow: no user during install, the +# first boot runs omarchy-provision-owner and creates it there # --reuse-base Skip the install phase if a base image already exists # --install-only Stop after producing the installed base image # --sync-omarchy DIR Push DIR's test/acceptance into the guest before running @@ -38,7 +38,7 @@ MEMORY=8192 INSTALL_TIMEOUT=2400 BOOT_TIMEOUT=600 ENCRYPT=false -OEM=false +PROVISION=false REUSE_BASE=false INSTALL_ONLY=false KEEP_RUNNING=false @@ -53,7 +53,7 @@ GUEST_HOSTNAME="omarchy-test" while (($#)); do case "$1" in --encrypt) ENCRYPT=true ;; - --oem) OEM=true ;; + --provision) PROVISION=true ;; --reuse-base) REUSE_BASE=true ;; --install-only) INSTALL_ONLY=true ;; --keep-running) KEEP_RUNNING=true ;; @@ -90,7 +90,7 @@ OVMF_VARS_TEMPLATE="/usr/share/edk2/x64/OVMF_VARS.4m.fd" BASE_NAME="$(basename "$ISO" .iso)" $ENCRYPT && BASE_NAME+="-encrypted" -$OEM && BASE_NAME+="-oem" +$PROVISION && BASE_NAME+="-provision" BASE_DIR="$REPO_DIR/test-runs/$BASE_NAME" RUN_DIR="$BASE_DIR/runs/$(date +%Y%m%d-%H%M%S)" BASE_DISK="$BASE_DIR/base.qcow2" @@ -606,12 +606,12 @@ audio-panel|meta_l-ctrl-a|omarchy-keyboard-panel|omarchy.audio' drive_configurator() { log "Driving the installer wizard" - # Keyboard is the first screen. Ctrl+C there is the hidden entry into OEM + # Keyboard is the first screen. Ctrl+C there is the hidden entry into deferred provisioning # mode; a normal install just selects the layout. wait_for_screen "keyboard layout" 300 capture_console "success-installer-01-keyboard-layout" - if $OEM; then + if $PROVISION; then press ctrl-c # arm "prepare for another owner" wait_for_screen "another owner" 30 capture_console "success-installer-02-prepare-confirm" @@ -699,30 +699,30 @@ drive_configurator() { capture_console "success-installer-14-install-started" } -# The OEM first boot runs the user form on tty1 (omarchy-oem-setup) before -# SDDM. Encrypted OEM installs auto-unlock from the staged keyfile, so there +# The deferred-provisioning first boot runs the user form on tty1 (omarchy-provision-owner) before +# SDDM. Encrypted deferred-provisioning installs auto-unlock from the staged keyfile, so there # is no LUKS prompt to answer here either way. -drive_oem_setup() { - log "Driving the OEM first-boot setup" +drive_provision_owner() { + log "Driving the deferred-provisioning first-boot setup" - # OEM defers the keyboard step to first boot; it comes before the user form. + # Deferred provisioning defers the keyboard step to first boot; it comes before the user form. # Pick English (UK) (one row above the preselected English (US)) so the # verifier can prove the deferred step applied a non-default layout — UK # leaves every letter identical to US, so the typed password (sent as raw # keycodes) still matches the literal one the harness pipes to sudo. wait_for_screen "keyboard layout" 600 press up - capture_console "success-oem-01-keyboard-layout-uk" + capture_console "success-provision-01-keyboard-layout-uk" press ret wait_for_screen "Username" 120 - capture_console "success-oem-02-username" + capture_console "success-provision-02-username" type_text "$GUEST_USER" press ret wait_for_screen "Password" 60 type_text "$GUEST_PASSWORD" - capture_console "success-oem-03-password" + capture_console "success-provision-03-password" press ret wait_for_screen "Conf *irm" 60 @@ -740,16 +740,16 @@ drive_oem_setup() { type_text "test" press shift-apostrophe type_text "omarchy.org" - capture_console "success-oem-04-email" + capture_console "success-provision-04-email" press ret wait_for_screen "look right" 60 - capture_console "success-oem-05-review" + capture_console "success-provision-05-review" press ret # Finalization (offline Node unpack, LUKS re-key on encrypted installs) # runs before SDDM appears. - capture_console "success-oem-06-finalizing" + capture_console "success-provision-06-finalizing" } wait_for_install() { @@ -952,30 +952,30 @@ install_phase() { drive_configurator wait_for_install - if $OEM; then - drive_oem_setup + if $PROVISION; then + drive_provision_owner elif $ENCRYPT; then unlock_luks fi bootstrap_ssh # SSH can come up (console bootstrap logs in on a spare TTY) while - # omarchy-oem-setup is still finalizing the user and re-keying LUKS. Saving + # omarchy-provision-owner is still finalizing the user and re-keying LUKS. Saving # the base mid-finalization produces a flaky image, so wait for the pending # flag to clear before powering off. - if $OEM; then - log "Waiting for OEM first-boot setup to finish" + if $PROVISION; then + log "Waiting for provisioning first-boot setup to finish" local waited=0 - while ssh_guest "test -f /var/lib/omarchy/oem/pending" 2>/dev/null; do + while ssh_guest "test -f /var/lib/omarchy/provisioning/pending" 2>/dev/null; do if ((waited >= 900)); then - capture_console "failure-oem-finalization-timeout" - echo "Timed out waiting for OEM first-boot setup to complete" >&2 + capture_console "failure-provision-finalization-timeout" + echo "Timed out waiting for provisioning first-boot setup to complete" >&2 return 1 fi sleep 10 ((waited += 10)) done - capture_console "success-oem-06-setup-complete" + capture_console "success-provision-06-setup-complete" fi ssh_guest "echo $GUEST_PASSWORD | sudo -S cat /var/log/omarchy-install-timing.json 2>/dev/null" \ diff --git a/configs/airootfs/root/.automated_script.sh b/configs/airootfs/root/.automated_script.sh index 0ba52c3..6057034 100644 --- a/configs/airootfs/root/.automated_script.sh +++ b/configs/airootfs/root/.automated_script.sh @@ -118,4 +118,4 @@ rm -f /run/omarchy-install/state.json --encrypt-file /root/user_encrypt_installation.txt \ --authorized-keys-file /root/authorized_keys \ --tailscale-authkey-file /root/tailscale_authkey \ - --oem-file /root/oem + --defer-provisioning-file /root/defer-provisioning diff --git a/configs/airootfs/root/configurator b/configs/airootfs/root/configurator index d510f6f..606fe2b 100644 --- a/configs/airootfs/root/configurator +++ b/configs/airootfs/root/configurator @@ -75,13 +75,13 @@ notice() { keyboard_form() { # The Ctrl+C hint sits directly under the header (no blank line between), # matching the disk-overwrite screen's unencrypted offer. Ctrl+C on the first - # screen is the hidden entry into OEM mode; $1 is "true" only for that first - # invocation, so the review-loop re-edit can't flip a user install into OEM. - local allow_oem="${1:-false}" + # screen is the hidden entry into deferred provisioning; $1 is "true" only for that first + # invocation, so the review-loop re-edit can't flip a user install into it. + local allow_defer_provisioning="${1:-false}" clear_logo echo say "Let's setup your machine..." - if $allow_oem; then + if $allow_defer_provisioning; then say --foreground 8 "Press Ctrl+C to prepare this machine for another owner." fi echo @@ -139,9 +139,9 @@ Ukrainian|ua' choice=$(printf '%s\n' "$keyboards" | cut -d'|' -f1 | gum choose --height 10 --selected "English (US)" --header "Select keyboard layout") local status=$? - if $allow_oem && (( status == 130 )); then + if $allow_defer_provisioning && (( status == 130 )); then if confirm_prepare_for_another_owner; then - oem_install=true + defer_provisioning=true return 0 fi keyboard_form true # declined: back to the keyboard picker @@ -171,9 +171,9 @@ confirm_prepare_for_another_owner() { "Prepare this machine for another owner?" } -# The user step. OEM installs skip it entirely — the machine's first owner +# The user step. Deferred-provisioning installs skip it entirely — the machine's first owner # picks their keyboard and creates their user in the first-boot setup, so the -# OEM operator sets nothing user-specific. +# operator sets nothing user-specific. user_form() { step "Let's setup your user account..." @@ -386,14 +386,14 @@ wait_for_device() { # Save the configurator answers the installer consumes: git identity and # encryption choice as plain files, account details as archinstall credentials. -# OEM installs defer the user to first boot: no credentials, and on encrypted +# Deferred-provisioning installs defer the user to first boot: no credentials, and on encrypted # targets the orchestrator generates a throwaway LUKS passphrase itself. write_user_files() { echo "$full_name" >user_full_name.txt echo "$email_address" >user_email_address.txt printf '%s\n' "$encrypt_installation" >user_encrypt_installation.txt - if $oem_install; then + if $defer_provisioning; then cat <<-_EOF_ >user_credentials.json { "users": [] @@ -895,7 +895,7 @@ confirm_disk_overwrite() { # Decide what and how to install: sets install_target (full_disk|free_space), # install_target and encrypt_installation. Nothing is written to disk yet — -# the user step runs between this and the execution below. (OEM mode never +# the user step runs between this and the execution below. (deferred provisioning never # reaches here; it's armed by Ctrl+C on the first screen and goes straight to # disk selection.) select_installation() { @@ -939,15 +939,15 @@ select_installation() { done } -# STEP 1: KEYBOARD (Ctrl+C here arms OEM mode and jumps to disk selection) +# STEP 1: KEYBOARD (Ctrl+C here arms deferred provisioning and jumps to disk selection) -oem_install=false +defer_provisioning=false install_target="full_disk" keyboard_form true -if $oem_install; then - # OEM: no user setup, and the keyboard is deferred to the machine's first +if $defer_provisioning; then + # Deferred provisioning: no user setup, and the keyboard is deferred to the machine's first # boot. A defined default keymap so it boots before then; the owner picks # their real layout in the OOBE, which re-keys the console and, on encrypted # installs, rebuilds the initramfs so the LUKS prompt matches. @@ -961,7 +961,7 @@ if $oem_install; then timezone="UTC" # Jump straight to disk selection + overwrite confirmation (encrypted by - # default, Ctrl+C toggles unencrypted). OEM is always a full-disk install. + # default, Ctrl+C toggles unencrypted). deferred provisioning is always a full-disk install. disk_form until confirm_disk_overwrite; do disk_form @@ -1005,9 +1005,9 @@ main_partition_size=$((disk_size_in_mib - main_partition_start - gpt_backup_rese kernel_choice=$(detect_kernel) if [[ $encrypt_installation == true ]]; then - # OEM installs carry no password: the orchestrator generates a throwaway + # Deferred-provisioning installs carry no password: the orchestrator generates a throwaway # LUKS passphrase and stages it for the first-boot re-key. - if $oem_install; then + if $defer_provisioning; then encryption_password_line="" else encryption_password_line=", @@ -1037,7 +1037,7 @@ cat <<-_EOF_ >user_configuration.json "custom_commands": [], "omarchy_install": { "mode": "full_disk", - "oem": $oem_install, + "defer_provisioning": $defer_provisioning, "target_mount": "/mnt", "boot": { "esp_mount": "/boot", diff --git a/configs/airootfs/usr/local/bin/omarchy-cidata-load b/configs/airootfs/usr/local/bin/omarchy-cidata-load index cabc210..6578849 100755 --- a/configs/airootfs/usr/local/bin/omarchy-cidata-load +++ b/configs/airootfs/usr/local/bin/omarchy-cidata-load @@ -11,8 +11,8 @@ # Anything less than the pair means this isn't an autoinstall drive, and the # caller falls back to the wizard. # -# An `oem` marker file stands in for user_credentials.json: OEM installs defer -# user creation to first boot, so imaging rigs ship no credentials at all. +# A `defer-provisioning` marker file stands in for user_credentials.json: deferred-provisioning +# installs defer user creation to first boot, so imaging rigs ship no credentials. # # The optional argument prefixes every path the script touches; tests use it to # run against a sandbox instead of the live system. @@ -39,17 +39,17 @@ mkdir -p "$mnt" mount -o ro "$device" "$mnt" || exit 1 # Clear any inputs a previous load in this live session left in /root before -# copying the current drive: a stale `oem` marker or credentials file would -# otherwise force a fresh drive's config back into OEM mode or hand it an old +# copying the current drive: a stale `defer-provisioning` marker or credentials file would +# otherwise force a fresh drive back into deferred provisioning or hand it an old # LUKS passphrase. The interactive wizard writes these same names, but a # cidata load always precedes it, so nothing of the user's is discarded here. -optional_inputs=(user_credentials.json oem user_full_name.txt user_email_address.txt user_encrypt_installation.txt authorized_keys tailscale_authkey) +optional_inputs=(user_credentials.json defer-provisioning user_full_name.txt user_email_address.txt user_encrypt_installation.txt authorized_keys tailscale_authkey) for file in user_configuration.json "${optional_inputs[@]}"; do rm -f "$dest/$file" done loaded=1 -if [[ -f $mnt/user_configuration.json ]] && [[ -f $mnt/user_credentials.json || -f $mnt/oem ]]; then +if [[ -f $mnt/user_configuration.json ]] && [[ -f $mnt/user_credentials.json || -f $mnt/defer-provisioning ]]; then files=("$mnt"/user_configuration.json) for file in "${optional_inputs[@]}"; do [[ -f $mnt/$file ]] && files+=("$mnt/$file") diff --git a/configs/airootfs/usr/local/bin/omarchy-install-dashboard b/configs/airootfs/usr/local/bin/omarchy-install-dashboard index bcf1a3f..541a230 100755 --- a/configs/airootfs/usr/local/bin/omarchy-install-dashboard +++ b/configs/airootfs/usr/local/bin/omarchy-install-dashboard @@ -321,7 +321,7 @@ install_progress() { "Installing Arch + Omarchy") lo=65 hi=700 tau=150 pkg_signal=1 ;; "Configuring hibernation") lo=700 hi=720 tau=10 ;; "Configuring system") lo=720 hi=850 tau=120 ;; - "Staging OEM state") lo=850 hi=855 tau=5 ;; + "Staging provisioning") lo=850 hi=855 tau=5 ;; "Finalizing Limine boot") lo=855 hi=950 tau=90 ;; "Finalizing user") lo=950 hi=978 tau=30 ;; "Configuring login") lo=978 hi=988 tau=5 ;; diff --git a/configs/airootfs/usr/local/bin/omarchy-iso-install b/configs/airootfs/usr/local/bin/omarchy-iso-install index 84b0839..59aca95 100755 --- a/configs/airootfs/usr/local/bin/omarchy-iso-install +++ b/configs/airootfs/usr/local/bin/omarchy-iso-install @@ -18,7 +18,7 @@ while [[ $# -gt 0 ]]; do --encrypt-file) export OMARCHY_INSTALL_ENCRYPT_FILE="$2"; shift 2 ;; --authorized-keys-file) export OMARCHY_INSTALL_AUTHORIZED_KEYS_FILE="$2"; shift 2 ;; --tailscale-authkey-file) export OMARCHY_INSTALL_TAILSCALE_AUTHKEY_FILE="$2"; shift 2 ;; - --oem-file) export OMARCHY_INSTALL_OEM_FILE="$2"; shift 2 ;; + --defer-provisioning-file) export OMARCHY_INSTALL_DEFER_PROVISIONING_FILE="$2"; shift 2 ;; *) echo "omarchy-iso-install: unknown arg $1" >&2; exit 2 ;; esac done diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py index a189e50..9b5c50f 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/context.py @@ -26,7 +26,7 @@ class InstallContext: user_credentials: dict arch_config_path: Path omarchy_install: dict[str, Any] - oem: bool = False + defer_provisioning: bool = False target: Path = Path("/mnt") omarchy_path: Path = Path("/usr/share/omarchy") @@ -50,23 +50,23 @@ class InstallContext: user_configuration = json.loads(config_path.read_text()) omarchy_install = user_configuration.get("omarchy_install") or _default_omarchy_install(user_configuration) - # OEM mode: the whole system installs but user creation is deferred to - # first boot. Selected by the configurator (omarchy_install.oem) or by - # an `oem` marker file on an autoinstall drive, which also replaces the + # Deferred provisioning: the whole system installs but user creation is deferred to + # first boot. Selected by the configurator (omarchy_install.defer_provisioning) or by + # an `defer-provisioning` marker file on an autoinstall drive, which also replaces the # user_credentials.json requirement. - oem_marker = _optional_path(os.environ.get("OMARCHY_INSTALL_OEM_FILE")) - oem = bool(omarchy_install.get("oem")) or oem_marker is not None - omarchy_install["oem"] = oem + defer_provisioning_marker = _optional_path(os.environ.get("OMARCHY_INSTALL_DEFER_PROVISIONING_FILE")) + defer_provisioning = bool(omarchy_install.get("defer_provisioning")) or defer_provisioning_marker is not None + omarchy_install["defer_provisioning"] = defer_provisioning if creds_path.exists(): user_credentials = json.loads(creds_path.read_text()) - elif oem: + elif defer_provisioning: user_credentials = {"users": []} else: raise RuntimeError(f"credentials file missing: {creds_path}") - if oem: - # OEM promises no account exists until first boot. A supplied + if defer_provisioning: + # Deferred provisioning promises no account until first boot. A supplied # credentials file (a rig handing over its LUKS passphrase) must # not smuggle in users or a root password. encryption_password = user_credentials.get("encryption_password") @@ -77,7 +77,7 @@ class InstallContext: arch_configuration = dict(user_configuration) arch_configuration.pop("omarchy_install", None) - if oem: + if defer_provisioning: # The userless invariant covers the archinstall config too: a # combined/legacy config could carry account authentication that # archinstall would still act on (users, a root password). Strip @@ -87,14 +87,14 @@ class InstallContext: state_dir = Path(os.environ.get("OMARCHY_INSTALL_STATE_DIR", "/run/omarchy-install")) state_dir.mkdir(parents=True, exist_ok=True) - if oem: - # Encrypted OEM installs have no user password to protect LUKS + if defer_provisioning: + # Encrypted deferred-provisioning installs have no user password to protect LUKS # with. Generate a throwaway passphrase (staged for first-boot - # re-key by the stage_oem_state phase) and hand it to archinstall + # re-key by the stage_provisioning_state phase) and hand it to archinstall # through both places it may look: the disk_encryption block and # the credentials file. - _inject_oem_encryption_password(arch_configuration, user_credentials) - creds_path = state_dir / "oem-user_credentials.json" + _inject_provisioning_encryption_password(arch_configuration, user_credentials) + creds_path = state_dir / "provisioning-user_credentials.json" creds_path.write_text(json.dumps(user_credentials, indent=2) + "\n") creds_path.chmod(0o600) @@ -113,7 +113,7 @@ class InstallContext: user_credentials=user_credentials, arch_config_path=arch_config_path, omarchy_install=omarchy_install, - oem=oem, + defer_provisioning=defer_provisioning, state_dir=state_dir, ) disk_config = user_configuration.get("disk_config", {}) @@ -127,7 +127,7 @@ class InstallContext: users = self.user_credentials.get("users") or [] if users: return users[0]["username"] - if self.oem: + if self.defer_provisioning: return "" raise RuntimeError("user_credentials.json contains no users") @@ -145,7 +145,7 @@ class InstallContext: def _strip_account_fields(arch_configuration: dict) -> None: """Remove every field archinstall reads to create users or set a root - password, so an OEM install cannot ship a hidden provisioning account. + password, so it cannot ship a hidden provisioning account. Encryption material lives elsewhere (disk_config.disk_encryption) and is untouched.""" for key in ("!users", "!root-password", "root_enc_password", "users"): @@ -157,12 +157,12 @@ def _strip_account_fields(arch_configuration: dict) -> None: auth.pop(key, None) -def _inject_oem_encryption_password(arch_configuration: dict, user_credentials: dict) -> None: - """Ensure an encrypted OEM install has a LUKS passphrase archinstall can +def _inject_provisioning_encryption_password(arch_configuration: dict, user_credentials: dict) -> None: + """Ensure an encrypted deferred-provisioning install has a LUKS passphrase archinstall can use. Reuse one the input already carries (an autoinstall rig may supply its own); otherwise generate a throwaway. Mutates the disk_encryption block in place — arch_configuration shares it with user_configuration, so - the stage_oem_state phase can read the effective passphrase back.""" + the stage_provisioning_state phase can read the effective passphrase back.""" disk_encryption = (arch_configuration.get("disk_config") or {}).get("disk_encryption") if disk_encryption is None: return diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py index fedefb4..9b6ebf5 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/main.py @@ -34,7 +34,7 @@ def build_phases(ctx: InstallContext): arch_install_system, configure_hibernation, run_system_finalizer, - stage_oem_state, + stage_provisioning_state, finalize_limine_boot, run_chroot_finalizer, configure_dns_resolver, @@ -51,9 +51,9 @@ def build_phases(ctx: InstallContext): ("Installing Arch + Omarchy", arch_install_system), ("Configuring hibernation", configure_hibernation), ("Configuring system", run_system_finalizer), - # Before finalize_limine_boot: the OEM cryptkey drop-in and keyfile + # Before finalize_limine_boot: the deferred-provisioning cryptkey drop-in and keyfile # must be in place for the final UKI build. - ("Staging OEM state", stage_oem_state), + ("Staging provisioning", stage_provisioning_state), ("Finalizing Limine boot", finalize_limine_boot), ("Finalizing user", run_chroot_finalizer), ("Configuring login", configure_login), @@ -72,7 +72,7 @@ def main() -> int: error(f"Configuration error: {e}") return 2 - who = ctx.username or "OEM (user created at first boot)" + who = ctx.username or "deferred provisioning (user created at first boot)" info(f"Installing Omarchy for {who} → {ctx.target}") from .phases_impl import ( diff --git a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py index 5aa2c85..f0e4660 100644 --- a/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py +++ b/configs/airootfs/usr/share/omarchy-iso/orchestrator/phases_impl.py @@ -1136,8 +1136,8 @@ def _run_target_setup_command(ctx: InstallContext, cmd: list[str], *, user: str def run_system_finalizer(ctx: InstallContext) -> None: - if ctx.oem: - cmd = ["/usr/bin/omarchy-setup-system", "--oem", "--first-install"] + if ctx.defer_provisioning: + cmd = ["/usr/bin/omarchy-setup-system", "--defer-provisioning", "--first-install"] else: cmd = ["/usr/bin/omarchy-setup-system", "--install-user", ctx.username, "--first-install"] @@ -1149,66 +1149,66 @@ def run_system_finalizer(ctx: InstallContext) -> None: # ───────────────────────────────────────────────────────────────────────────── -# stage_oem_state: produce the on-disk "OEM state" the runtime's first-boot -# setup (omarchy-oem-setup) and factory reset (omarchy-system-factory-reset) consume. +# stage_provisioning_state: produce the on-disk "provisioning state" the runtime's first-boot +# setup (omarchy-provision-owner) and factory reset (omarchy-system-factory-reset) consume. # -# Every install stashes the bundled Node tarball in /var/lib/omarchy/oem/ -# so a later factory reset can finalize the new owner's user offline. OEM-mode +# Every install stashes the bundled Node tarball in /var/lib/omarchy/provisioning/ +# so a later factory reset can finalize the new owner's user offline. deferred-provisioning # installs additionally arm the first-boot setup service and, on encrypted # targets, stage the throwaway LUKS passphrase: the keyfile embedded in the -# initramfs auto-unlocks boot during the OEM window, and first-boot setup +# initramfs auto-unlocks boot during the provisioning window, and first-boot setup # re-keys the volume to the owner's password and removes it. # # Runs before finalize_limine_boot so the cryptkey cmdline drop-in and the # keyfile land in the final UKI build. # ───────────────────────────────────────────────────────────────────────────── -OEM_STATE_DIR = "var/lib/omarchy/oem" -OEM_KEYFILE = "etc/omarchy/oem.key" +PROVISION_STATE_DIR = "var/lib/omarchy/provisioning" +PROVISION_KEYFILE = "etc/omarchy/provisioning.key" NODE_PACKAGES_DIR = Path("/opt/packages") -def stage_oem_state(ctx: InstallContext) -> None: +def stage_provisioning_state(ctx: InstallContext) -> None: # World-readable: first-boot finalization reads the Node tarball as the # new user. The only secret inside (luks-key) is itself 0600 root. - oem_dir = ctx.target / OEM_STATE_DIR - oem_dir.mkdir(parents=True, exist_ok=True) - oem_dir.chmod(0o755) + provisioning_dir = ctx.target / PROVISION_STATE_DIR + provisioning_dir.mkdir(parents=True, exist_ok=True) + provisioning_dir.chmod(0o755) - _stage_node_tarball(ctx, oem_dir) + _stage_node_tarball(ctx, provisioning_dir) - if not ctx.oem: + if not ctx.defer_provisioning: return - service_src = ctx.target / "usr/share/omarchy/install/oem/omarchy-oem-setup.service" - setup_bin = ctx.target / "usr/bin/omarchy-oem-setup" + service_src = ctx.target / "usr/share/omarchy/install/provisioning/omarchy-provision-owner.service" + setup_bin = ctx.target / "usr/bin/omarchy-provision-owner" if not service_src.exists() or not setup_bin.exists(): raise RuntimeError( - "OEM install requested, but the installed Omarchy runtime does not ship " - "first-boot setup (omarchy-oem-setup + install/oem/omarchy-oem-setup.service). " - "Update the runtime package this ISO bundles before installing in OEM mode." + "deferred-provisioning install requested, but the installed Omarchy runtime does not ship " + "first-boot setup (omarchy-provision-owner + install/provisioning/omarchy-provision-owner.service). " + "Update the runtime package this ISO bundles before installing in deferred provisioning." ) - info("› arming first-boot OEM setup") - (oem_dir / "pending").touch() + info("› arming first-boot setup") + (provisioning_dir / "pending").touch() - unit_dst = ctx.target / "etc/systemd/system/omarchy-oem-setup.service" + unit_dst = ctx.target / "etc/systemd/system/omarchy-provision-owner.service" unit_dst.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(service_src, unit_dst) wants_dir = ctx.target / "etc/systemd/system/multi-user.target.wants" wants_dir.mkdir(parents=True, exist_ok=True) - link = wants_dir / "omarchy-oem-setup.service" + link = wants_dir / "omarchy-provision-owner.service" link.unlink(missing_ok=True) - link.symlink_to("/etc/systemd/system/omarchy-oem-setup.service") + link.symlink_to("/etc/systemd/system/omarchy-provision-owner.service") - if _oem_install_encrypted(ctx): - _stage_oem_luks_unlock(ctx, oem_dir) + if _provision_install_encrypted(ctx): + _stage_provisioning_luks_unlock(ctx, provisioning_dir) -def _stage_node_tarball(ctx: InstallContext, oem_dir) -> None: +def _stage_node_tarball(ctx: InstallContext, provisioning_dir) -> None: tarballs = sorted(NODE_PACKAGES_DIR.glob("node-v*-linux-x64.tar.gz")) if not tarballs: - # Hard error on every install, not just OEM: the stash is what lets a + # Hard error on every install, not just deferred-provisioning installs: the stash is what lets a # later factory reset finalize the next owner offline, and an ISO # build always bundles the tarball — its absence means a broken build. raise RuntimeError( @@ -1216,7 +1216,7 @@ def _stage_node_tarball(ctx: InstallContext, oem_dir) -> None: "and factory reset could not finalize a user offline" ) - packages_dir = oem_dir / "packages" + packages_dir = provisioning_dir / "packages" packages_dir.mkdir(parents=True, exist_ok=True) target_tarball = packages_dir / tarballs[0].name if not target_tarball.exists(): @@ -1224,7 +1224,7 @@ def _stage_node_tarball(ctx: InstallContext, oem_dir) -> None: shutil.copy2(tarballs[0], target_tarball) -def _oem_install_encrypted(ctx: InstallContext) -> bool: +def _provision_install_encrypted(ctx: InstallContext) -> bool: if _storage_intent(ctx).get("luks_uuid"): return True disk_encryption = (ctx.user_configuration.get("disk_config") or {}).get("disk_encryption") @@ -1233,43 +1233,43 @@ def _oem_install_encrypted(ctx: InstallContext) -> bool: return ctx.encrypt -def _oem_encryption_password(ctx: InstallContext) -> str | None: +def _provision_encryption_password(ctx: InstallContext) -> str | None: disk_encryption = (ctx.user_configuration.get("disk_config") or {}).get("disk_encryption") or {} return disk_encryption.get("encryption_password") or ctx.user_credentials.get("encryption_password") -def _stage_oem_luks_unlock(ctx: InstallContext, oem_dir) -> None: - password = _oem_encryption_password(ctx) +def _stage_provisioning_luks_unlock(ctx: InstallContext, provisioning_dir) -> None: + password = _provision_encryption_password(ctx) if not password: - # Full-disk OEM installs get a generated passphrase injected by + # Full-disk deferred-provisioning installs get a generated passphrase injected by # InstallContext; only a pre-mounted (rig-partitioned) LUKS target can # land here, and it must hand over the passphrase it formatted with. raise RuntimeError( - "OEM install on a pre-encrypted target requires the LUKS passphrase " + "deferred-provisioning install on a pre-encrypted target requires the LUKS passphrase " "in user_credentials.json (encryption_password) so first boot can re-key" ) - info("› staging LUKS auto-unlock for the OEM window") + info("› staging LUKS auto-unlock for the provisioning window") # Byte-for-byte the slot passphrase: no trailing newline anywhere. - luks_key = oem_dir / "luks-key" + luks_key = provisioning_dir / "luks-key" luks_key.write_text(password) luks_key.chmod(0o600) - keyfile = ctx.target / OEM_KEYFILE + keyfile = ctx.target / PROVISION_KEYFILE keyfile.parent.mkdir(parents=True, exist_ok=True) keyfile.write_text(password) keyfile.chmod(0o600) - cmdline_dropin = ctx.target / "etc/limine-entry-tool.d/99-omarchy-oem-unlock.conf" + cmdline_dropin = ctx.target / "etc/limine-entry-tool.d/99-omarchy-provisioning-unlock.conf" cmdline_dropin.parent.mkdir(parents=True, exist_ok=True) cmdline_dropin.write_text( - 'KERNEL_CMDLINE[default]+=" cryptkey=rootfs:/etc/omarchy/oem.key"\n' + 'KERNEL_CMDLINE[default]+=" cryptkey=rootfs:/etc/omarchy/provisioning.key"\n' ) - files_dropin = ctx.target / "etc/mkinitcpio.conf.d/99-omarchy-oem-key.conf" + files_dropin = ctx.target / "etc/mkinitcpio.conf.d/99-omarchy-provisioning-key.conf" files_dropin.parent.mkdir(parents=True, exist_ok=True) - files_dropin.write_text("FILES+=(/etc/omarchy/oem.key)\n") + files_dropin.write_text("FILES+=(/etc/omarchy/provisioning.key)\n") def finalize_limine_boot(ctx: InstallContext) -> None: @@ -1365,8 +1365,8 @@ def _limine_kernel_cmdline(config_text: str) -> str: def run_chroot_finalizer(ctx: InstallContext) -> None: - if ctx.oem: - info("› OEM install: user finalization deferred to first boot") + if ctx.defer_provisioning: + info("› deferred-provisioning install: user finalization deferred to first boot") return _run_target_setup_command( @@ -1415,18 +1415,18 @@ def configure_login(ctx: InstallContext) -> None: ) autologin_conf = sddm_dir / "autologin.conf" - if ctx.encrypt and not ctx.oem: + if ctx.encrypt and not ctx.defer_provisioning: autologin_conf.write_text( "[Autologin]\n" f"User={ctx.username}\n" "Session=omarchy.desktop\n" ) else: - # OEM installs have no user yet; omarchy-oem-setup writes autologin + # deferred-provisioning installs have no user yet; omarchy-provision-owner writes autologin # and SDDM state at first boot once the owner exists. autologin_conf.unlink(missing_ok=True) - if not ctx.oem: + if not ctx.defer_provisioning: state_dir = ctx.target / "var" / "lib" / "sddm" state_dir.mkdir(parents=True, exist_ok=True) (state_dir / "state.conf").write_text( @@ -1460,14 +1460,14 @@ def configure_ssh_access(ctx: InstallContext) -> None: keys = _authorized_keys(ctx.authorized_keys_path) - if ctx.oem: - # No user to authorize yet. Stage the keys in OEM state for - # omarchy-oem-setup to install once first boot creates the owner, and + if ctx.defer_provisioning: + # No user to authorize yet. Stage the keys in provisioning state for + # omarchy-provision-owner to install once first boot creates the owner, and # still open the door (sshd + ufw) below. info(f"› staging {len(keys)} SSH key(s) for the first-boot user") - oem_dir = ctx.target / OEM_STATE_DIR - oem_dir.mkdir(parents=True, exist_ok=True) - staged = oem_dir / "authorized_keys" + provisioning_dir = ctx.target / PROVISION_STATE_DIR + provisioning_dir.mkdir(parents=True, exist_ok=True) + staged = provisioning_dir / "authorized_keys" staged.write_text("".join(f"{key}\n" for key in keys)) staged.chmod(0o600) else: @@ -1695,33 +1695,33 @@ def validate_boot(ctx: InstallContext) -> None: if ctx.is_protected: _validate_pre_mounted_filesystems(ctx) - if ctx.oem: - _validate_oem_state(ctx) + if ctx.defer_provisioning: + _validate_provisioning_state(ctx) -def _validate_oem_state(ctx: InstallContext) -> None: - """An OEM install that boots without a working first-boot setup is a +def _validate_provisioning_state(ctx: InstallContext) -> None: + """An deferred-provisioning install that boots without a working first-boot setup is a user-less brick; insist the armed state is complete before reboot.""" - oem_dir = ctx.target / OEM_STATE_DIR - if not (oem_dir / "pending").exists(): - raise RuntimeError(f"OEM install but {oem_dir / 'pending'} is missing") + provisioning_dir = ctx.target / PROVISION_STATE_DIR + if not (provisioning_dir / "pending").exists(): + raise RuntimeError(f"deferred-provisioning install but {provisioning_dir / 'pending'} is missing") - link = ctx.target / "etc/systemd/system/multi-user.target.wants/omarchy-oem-setup.service" + link = ctx.target / "etc/systemd/system/multi-user.target.wants/omarchy-provision-owner.service" if not link.is_symlink(): - raise RuntimeError("OEM install but omarchy-oem-setup.service is not enabled") + raise RuntimeError("deferred-provisioning install but omarchy-provision-owner.service is not enabled") - if not list((oem_dir / "packages").glob("node-v*.tar.gz")): - raise RuntimeError(f"OEM install but no Node tarball staged in {oem_dir / 'packages'}") + if not list((provisioning_dir / "packages").glob("node-v*.tar.gz")): + raise RuntimeError(f"deferred-provisioning install but no Node tarball staged in {provisioning_dir / 'packages'}") - if _oem_install_encrypted(ctx): - for required in (oem_dir / "luks-key", ctx.target / OEM_KEYFILE): + if _provision_install_encrypted(ctx): + for required in (provisioning_dir / "luks-key", ctx.target / PROVISION_KEYFILE): if not required.exists(): - raise RuntimeError(f"encrypted OEM install but {required} is missing") + raise RuntimeError(f"encrypted deferred-provisioning install but {required} is missing") limine_conf_text = ( ctx.target / _boot_intent(ctx)["esp_mount"].lstrip("/") / "limine.conf" ).read_text() if "cryptkey=rootfs:" not in limine_conf_text: - raise RuntimeError("encrypted OEM install but limine.conf has no cryptkey= for auto-unlock") + raise RuntimeError("encrypted deferred-provisioning install but limine.conf has no cryptkey= for auto-unlock") def _assert_boot_hooks_restored(ctx: InstallContext) -> None: @@ -1835,15 +1835,15 @@ def create_factory_snapshot(ctx: InstallContext) -> None: # Provisioning credentials staged for THIS deployment's first boot must not # survive into the factory image: a reset years later would otherwise hand the # next owner the original deployment's SSH keys or rejoin its tailnet, and a -# stale OEM LUKS key (dead after the first re-key) has no business lingering. +# stale LUKS key (dead after the first re-key) has no business lingering. # The mkinitcpio/cmdline drop-ins go with the keyfile — a reset rebuild would # otherwise fail on FILES pointing at a scrubbed path. FACTORY_SCRUB_PATHS = ( - "var/lib/omarchy/oem/authorized_keys", - "var/lib/omarchy/oem/luks-key", - "etc/omarchy/oem.key", - "etc/limine-entry-tool.d/99-omarchy-oem-unlock.conf", - "etc/mkinitcpio.conf.d/99-omarchy-oem-key.conf", + "var/lib/omarchy/provisioning/authorized_keys", + "var/lib/omarchy/provisioning/luks-key", + "etc/omarchy/provisioning.key", + "etc/limine-entry-tool.d/99-omarchy-provisioning-unlock.conf", + "etc/mkinitcpio.conf.d/99-omarchy-provisioning-key.conf", "etc/tailscale/authkey", "etc/systemd/system/omarchy-tailscale-join.service", "etc/systemd/system/multi-user.target.wants/omarchy-tailscale-join.service", diff --git a/test/cidata-load-test.sh b/test/cidata-load-test.sh index 67e798b..4914dc6 100644 --- a/test/cidata-load-test.sh +++ b/test/cidata-load-test.sh @@ -124,56 +124,56 @@ run_load || fail "required pair plus authorized_keys loads" [[ ! -e $sandbox/root/tailscale_authkey ]] || fail "absent tailscale_authkey is not copied" pass "present optional files are copied, absent ones skipped" -# An oem marker replaces user_credentials.json: OEM installs defer user +# A defer-provisioning marker replaces user_credentials.json: deferred-provisioning installs # creation to first boot, so imaging rigs ship no credentials at all. new_sandbox attach_drive cidata echo '{"disk_config": {}}' >"$sandbox/media/user_configuration.json" -: >"$sandbox/media/oem" -run_load || fail "config plus oem marker loads" -[[ -f $sandbox/root/oem ]] || fail "oem marker is copied" -[[ ! -e $sandbox/root/user_credentials.json ]] || fail "oem drive copies no credentials" -pass "oem marker stands in for credentials" +: >"$sandbox/media/defer-provisioning" +run_load || fail "config plus defer-provisioning marker loads" +[[ -f $sandbox/root/defer-provisioning ]] || fail "defer-provisioning marker is copied" +[[ ! -e $sandbox/root/user_credentials.json ]] || fail "defer-provisioning drive copies no credentials" +pass "defer-provisioning marker stands in for credentials" -# The oem marker and credentials can coexist (rig supplies its own LUKS +# The defer-provisioning marker and credentials can coexist (rig supplies its own LUKS # passphrase in the credentials file); both are copied. new_sandbox attach_drive cidata write_required_pair -: >"$sandbox/media/oem" -run_load || fail "oem marker plus credentials loads" -[[ -f $sandbox/root/oem && -f $sandbox/root/user_credentials.json ]] || fail "both oem marker and credentials are copied" -pass "oem marker plus credentials copies both" +: >"$sandbox/media/defer-provisioning" +run_load || fail "defer-provisioning marker plus credentials loads" +[[ -f $sandbox/root/defer-provisioning && -f $sandbox/root/user_credentials.json ]] || fail "both defer-provisioning marker and credentials are copied" +pass "defer-provisioning marker plus credentials copies both" -# An oem marker without the configuration is still not an autoinstall drive. +# An defer-provisioning marker without the configuration is still not an autoinstall drive. new_sandbox attach_drive cidata -: >"$sandbox/media/oem" -! run_load || fail "oem marker alone is not an autoinstall drive" -[[ ! -e $sandbox/root/oem ]] || fail "oem marker alone copies nothing" -pass "oem marker alone falls back to the wizard" +: >"$sandbox/media/defer-provisioning" +! run_load || fail "defer-provisioning marker alone is not an autoinstall drive" +[[ ! -e $sandbox/root/defer-provisioning ]] || fail "defer-provisioning marker alone copies nothing" +pass "defer-provisioning marker alone falls back to the wizard" -# Stale OEM inputs from a previous load in the same session are cleared before -# the current drive is copied: a normal drive must not inherit an old oem +# Stale deferred-provisioning inputs from a previous load in the same session are cleared before +# the current drive is copied: a normal drive must not inherit an old defer-provisioning # marker or credentials. new_sandbox attach_drive cidata write_required_pair -: >"$sandbox/root/oem" # leftover from a prior OEM load +: >"$sandbox/root/defer-provisioning" # leftover from a prior deferred-provisioning load echo 'old-keys' >"$sandbox/root/authorized_keys" -run_load || fail "normal drive after a stale OEM load loads" -[[ ! -e $sandbox/root/oem ]] || fail "stale oem marker is cleared" +run_load || fail "normal drive after a stale defer-provisioning load loads" +[[ ! -e $sandbox/root/defer-provisioning ]] || fail "stale defer-provisioning marker is cleared" [[ ! -e $sandbox/root/authorized_keys ]] || fail "stale optional inputs are cleared" -pass "stale OEM inputs are cleared before loading a normal drive" +pass "stale deferred-provisioning inputs are cleared before loading a normal drive" # A drive that isn't an autoinstall drive at all still clears stale inputs so # the wizard that follows doesn't inherit them. new_sandbox attach_drive cidata echo '{"disk_config": {}}' >"$sandbox/media/user_configuration.json" # half a pair -: >"$sandbox/root/oem" +: >"$sandbox/root/defer-provisioning" ! run_load || fail "half-pair drive still falls back" -[[ ! -e $sandbox/root/oem ]] || fail "stale oem marker cleared even on fallback" +[[ ! -e $sandbox/root/defer-provisioning ]] || fail "stale defer-provisioning marker cleared even on fallback" pass "stale inputs are cleared even when falling back to the wizard" # Half the required pair is not an autoinstall drive: unmount and fall back. diff --git a/test/test_configure_ssh_access.py b/test/test_configure_ssh_access.py index da60318..50050d2 100644 --- a/test/test_configure_ssh_access.py +++ b/test/test_configure_ssh_access.py @@ -60,7 +60,7 @@ class ConfigureSshAccessTest(unittest.TestCase): if authorized_keys is not None: authorized_keys_path = self.target / "authorized_keys" authorized_keys_path.write_text(authorized_keys) - return types.SimpleNamespace(target=self.target, username="jeff", authorized_keys_path=authorized_keys_path, oem=False) + return types.SimpleNamespace(target=self.target, username="jeff", authorized_keys_path=authorized_keys_path, defer_provisioning=False) def configure(self, **kwargs): phases_impl.configure_ssh_access(self.ctx(**kwargs)) diff --git a/test/test_oem_state.py b/test/test_provisioning_state.py similarity index 71% rename from test/test_oem_state.py rename to test/test_provisioning_state.py index d683718..d2df6e7 100644 --- a/test/test_oem_state.py +++ b/test/test_provisioning_state.py @@ -1,7 +1,7 @@ -"""Unit tests for OEM install support in the orchestrator. +"""Unit tests for deferred-provisioning install support in the orchestrator. -Covers the InstallContext OEM plumbing (marker file, credential-less input, -throwaway LUKS passphrase injection) and the stage_oem_state / +Covers the InstallContext deferred-provisioning plumbing (marker file, credential-less input, +throwaway LUKS passphrase injection) and the stage_provisioning_state / create_factory_snapshot / configure_login phases against a temp target with subprocess mocked out. """ @@ -26,7 +26,7 @@ from orchestrator import phases_impl # noqa: E402 from orchestrator.context import InstallContext # noqa: E402 -class ContextOemTest(unittest.TestCase): +class ContextDeferProvisioningTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.addCleanup(self.tmp.cleanup) @@ -48,51 +48,51 @@ class ContextOemTest(unittest.TestCase): def from_env(self, **extra_env): env = {**self.env, **extra_env} with mock.patch.dict(os.environ, env, clear=False): - for key in ("OMARCHY_INSTALL_OEM_FILE",): + for key in ("OMARCHY_INSTALL_DEFER_PROVISIONING_FILE",): if key not in env: os.environ.pop(key, None) return InstallContext.from_env() - def base_config(self, oem=None, disk_encryption=None): + def base_config(self, defer_provisioning=None, disk_encryption=None): config = { "disk_config": {"config_type": "default_layout"}, "omarchy_install": {"mode": "full_disk", "target_mount": "/mnt"}, } - if oem is not None: - config["omarchy_install"]["oem"] = oem + if defer_provisioning is not None: + config["omarchy_install"]["defer_provisioning"] = defer_provisioning if disk_encryption is not None: config["disk_config"]["disk_encryption"] = disk_encryption return config - def test_oem_flag_from_config(self): - self.write_config(self.base_config(oem=True)) + def test_defer_provisioning_flag_from_config(self): + self.write_config(self.base_config(defer_provisioning=True)) ctx = self.from_env() - self.assertTrue(ctx.oem) + self.assertTrue(ctx.defer_provisioning) self.assertEqual(ctx.user_credentials, {"users": []}) self.assertEqual(ctx.username, "") - def test_oem_marker_file_arms_oem_without_credentials(self): + def test_marker_file_arms_deferred_provisioning_without_credentials(self): self.write_config(self.base_config()) - marker = self.dir / "oem" + marker = self.dir / "defer_provisioning" marker.touch() - ctx = self.from_env(OMARCHY_INSTALL_OEM_FILE=str(marker)) - self.assertTrue(ctx.oem) - self.assertTrue(ctx.omarchy_install["oem"]) + ctx = self.from_env(OMARCHY_INSTALL_DEFER_PROVISIONING_FILE=str(marker)) + self.assertTrue(ctx.defer_provisioning) + self.assertTrue(ctx.omarchy_install["defer_provisioning"]) - def test_absent_marker_file_is_not_oem(self): + def test_absent_marker_file_is_not_deferred_provisioning(self): self.write_config(self.base_config()) self.write_creds({"users": [{"username": "jeff"}]}) - ctx = self.from_env(OMARCHY_INSTALL_OEM_FILE=str(self.dir / "missing-oem")) - self.assertFalse(ctx.oem) + ctx = self.from_env(OMARCHY_INSTALL_DEFER_PROVISIONING_FILE=str(self.dir / "missing-defer_provisioning")) + self.assertFalse(ctx.defer_provisioning) self.assertEqual(ctx.username, "jeff") - def test_missing_credentials_without_oem_raises(self): + def test_missing_credentials_without_defer_provisioning_raises(self): self.write_config(self.base_config()) with self.assertRaisesRegex(RuntimeError, "credentials file missing"): self.from_env() - def test_oem_generates_throwaway_luks_passphrase(self): - self.write_config(self.base_config(oem=True, disk_encryption={ + def test_deferred_provisioning_generates_throwaway_luks_passphrase(self): + self.write_config(self.base_config(defer_provisioning=True, disk_encryption={ "encryption_type": "luks", "partitions": ["x"], })) ctx = self.from_env() @@ -110,8 +110,8 @@ class ContextOemTest(unittest.TestCase): synthesized = json.loads(ctx.creds_path.read_text()) self.assertEqual(synthesized["encryption_password"], password) - def test_oem_reuses_rig_supplied_passphrase(self): - self.write_config(self.base_config(oem=True, disk_encryption={ + def test_deferred_provisioning_reuses_rig_supplied_passphrase(self): + self.write_config(self.base_config(defer_provisioning=True, disk_encryption={ "encryption_type": "luks", "partitions": ["x"], })) self.write_creds({"users": [], "encryption_password": "rig-secret"}) @@ -121,9 +121,9 @@ class ContextOemTest(unittest.TestCase): "rig-secret", ) - def test_oem_discards_account_material_from_supplied_credentials(self): + def test_deferred_provisioning_discards_account_material(self): # A rig credentials file must not smuggle in users or a root password. - self.write_config(self.base_config(oem=True)) + self.write_config(self.base_config(defer_provisioning=True)) self.write_creds({ "users": [{"username": "backdoor", "enc_password": "x", "sudo": True}], "root_enc_password": "x", @@ -137,8 +137,8 @@ class ContextOemTest(unittest.TestCase): self.assertEqual(synthesized["users"], []) self.assertNotIn("root_enc_password", synthesized) - def test_oem_strips_account_fields_from_arch_config(self): - config = self.base_config(oem=True) + def test_defer_provisioning_strips_account_fields(self): + config = self.base_config(defer_provisioning=True) config["!users"] = [{"username": "rig"}] config["!root-password"] = "hunter2" config["root_enc_password"] = "x" @@ -151,8 +151,8 @@ class ContextOemTest(unittest.TestCase): self.assertNotIn(key, arch_config) self.assertEqual(arch_config["auth_config"], {}) - def test_oem_unencrypted_touches_nothing(self): - self.write_config(self.base_config(oem=True)) + def test_deferred_provisioning_unencrypted_touches_nothing(self): + self.write_config(self.base_config(defer_provisioning=True)) ctx = self.from_env() self.assertNotIn("encryption_password", ctx.user_credentials) @@ -160,10 +160,10 @@ class ContextOemTest(unittest.TestCase): def make_ctx(target, **overrides): defaults = dict( target=target, - oem=True, + defer_provisioning=True, encrypt=False, username="", - omarchy_install={"mode": "full_disk", "oem": True, "storage": {}}, + omarchy_install={"mode": "full_disk", "defer_provisioning": True, "storage": {}}, user_configuration={"disk_config": {}}, user_credentials={"users": []}, state_dir=target / "state", @@ -173,7 +173,7 @@ def make_ctx(target, **overrides): return types.SimpleNamespace(**defaults) -class StageOemStateTest(unittest.TestCase): +class StageProvisioningStateTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.addCleanup(self.tmp.cleanup) @@ -192,63 +192,63 @@ class StageOemStateTest(unittest.TestCase): node_patch.start() self.addCleanup(node_patch.stop) - def install_runtime_oem_support(self): - service = self.target / "usr/share/omarchy/install/oem/omarchy-oem-setup.service" + def install_runtime_provisioning_support(self): + service = self.target / "usr/share/omarchy/install/provisioning/omarchy-provision-owner.service" service.parent.mkdir(parents=True, exist_ok=True) service.write_text("[Unit]\n") - setup_bin = self.target / "usr/bin/omarchy-oem-setup" + setup_bin = self.target / "usr/bin/omarchy-provision-owner" setup_bin.parent.mkdir(parents=True, exist_ok=True) setup_bin.write_text("#!/bin/bash\n") - def oem_dir(self): - return self.target / "var/lib/omarchy/oem" + def provisioning_dir(self): + return self.target / "var/lib/omarchy/provisioning" - def test_non_oem_install_stages_only_the_node_tarball(self): - ctx = make_ctx(self.target, oem=False) - phases_impl.stage_oem_state(ctx) + def test_normal_install_stages_only_the_node_tarball(self): + ctx = make_ctx(self.target, defer_provisioning=False) + phases_impl.stage_provisioning_state(ctx) - self.assertTrue((self.oem_dir() / "packages/node-v24.0.0-linux-x64.tar.gz").exists()) - self.assertFalse((self.oem_dir() / "pending").exists()) - self.assertFalse((self.target / "etc/systemd/system/omarchy-oem-setup.service").exists()) + self.assertTrue((self.provisioning_dir() / "packages/node-v24.0.0-linux-x64.tar.gz").exists()) + self.assertFalse((self.provisioning_dir() / "pending").exists()) + self.assertFalse((self.target / "etc/systemd/system/omarchy-provision-owner.service").exists()) - def test_oem_against_runtime_without_support_fails_clearly(self): + def test_deferred_provisioning_against_runtime_without_support_fails_clearly(self): ctx = make_ctx(self.target) with self.assertRaisesRegex(RuntimeError, "does not ship"): - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) - def test_oem_without_node_tarball_fails(self): + def test_deferred_provisioning_without_node_tarball_fails(self): for tarball in self.packages.glob("*.tar.gz"): tarball.unlink() - self.install_runtime_oem_support() + self.install_runtime_provisioning_support() ctx = make_ctx(self.target) with self.assertRaisesRegex(RuntimeError, "Node tarball"): - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) def test_missing_node_tarball_fails_normal_installs_too(self): # The stash is what makes a later factory reset work offline. for tarball in self.packages.glob("*.tar.gz"): tarball.unlink() - ctx = make_ctx(self.target, oem=False) + ctx = make_ctx(self.target, defer_provisioning=False) with self.assertRaisesRegex(RuntimeError, "Node tarball"): - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) - def test_oem_arms_first_boot_setup(self): - self.install_runtime_oem_support() + def test_deferred_provisioning_arms_first_boot_setup(self): + self.install_runtime_provisioning_support() ctx = make_ctx(self.target) - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) - self.assertTrue((self.oem_dir() / "pending").exists()) - self.assertTrue((self.target / "etc/systemd/system/omarchy-oem-setup.service").exists()) - link = self.target / "etc/systemd/system/multi-user.target.wants/omarchy-oem-setup.service" + self.assertTrue((self.provisioning_dir() / "pending").exists()) + self.assertTrue((self.target / "etc/systemd/system/omarchy-provision-owner.service").exists()) + link = self.target / "etc/systemd/system/multi-user.target.wants/omarchy-provision-owner.service" self.assertTrue(link.is_symlink()) self.assertEqual( - os.readlink(link), "/etc/systemd/system/omarchy-oem-setup.service" + os.readlink(link), "/etc/systemd/system/omarchy-provision-owner.service" ) # Unencrypted: no LUKS staging. - self.assertFalse((self.oem_dir() / "luks-key").exists()) + self.assertFalse((self.provisioning_dir() / "luks-key").exists()) - def test_oem_encrypted_stages_luks_auto_unlock(self): - self.install_runtime_oem_support() + def test_deferred_provisioning_encrypted_stages_luks_auto_unlock(self): + self.install_runtime_provisioning_support() ctx = make_ctx( self.target, user_configuration={"disk_config": {"disk_encryption": { @@ -256,30 +256,30 @@ class StageOemStateTest(unittest.TestCase): "encryption_password": "throwaway-secret", }}}, ) - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) # Byte-for-byte the slot passphrase — no trailing newline. - self.assertEqual((self.oem_dir() / "luks-key").read_text(), "throwaway-secret") - keyfile = self.target / "etc/omarchy/oem.key" + self.assertEqual((self.provisioning_dir() / "luks-key").read_text(), "throwaway-secret") + keyfile = self.target / "etc/omarchy/provisioning.key" self.assertEqual(keyfile.read_text(), "throwaway-secret") self.assertEqual(keyfile.stat().st_mode & 0o777, 0o600) - cmdline = (self.target / "etc/limine-entry-tool.d/99-omarchy-oem-unlock.conf").read_text() - self.assertIn("cryptkey=rootfs:/etc/omarchy/oem.key", cmdline) - files = (self.target / "etc/mkinitcpio.conf.d/99-omarchy-oem-key.conf").read_text() - self.assertIn("FILES+=(/etc/omarchy/oem.key)", files) + cmdline = (self.target / "etc/limine-entry-tool.d/99-omarchy-provisioning-unlock.conf").read_text() + self.assertIn("cryptkey=rootfs:/etc/omarchy/provisioning.key", cmdline) + files = (self.target / "etc/mkinitcpio.conf.d/99-omarchy-provisioning-key.conf").read_text() + self.assertIn("FILES+=(/etc/omarchy/provisioning.key)", files) - def test_oem_pre_encrypted_without_passphrase_fails(self): - self.install_runtime_oem_support() + def test_deferred_provisioning_pre_encrypted_without_passphrase_fails(self): + self.install_runtime_provisioning_support() ctx = make_ctx( self.target, - omarchy_install={"mode": "protected", "oem": True, "storage": {"luks_uuid": "abc"}}, + omarchy_install={"mode": "protected", "defer_provisioning": True, "storage": {"luks_uuid": "abc"}}, ) with self.assertRaisesRegex(RuntimeError, "passphrase"): - phases_impl.stage_oem_state(ctx) + phases_impl.stage_provisioning_state(ctx) -class ConfigureLoginOemTest(unittest.TestCase): +class ConfigureLoginDeferProvisioningTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.addCleanup(self.tmp.cleanup) @@ -293,7 +293,7 @@ class ConfigureLoginOemTest(unittest.TestCase): run_patch.start() self.addCleanup(run_patch.stop) - def test_oem_login_leaves_user_state_to_first_boot(self): + def test_deferred_provisioning_login_leaves_user_state_to_first_boot(self): ctx = make_ctx(self.target, encrypt=True) phases_impl.configure_login(ctx) @@ -303,7 +303,7 @@ class ConfigureLoginOemTest(unittest.TestCase): self.assertTrue(any("sddm.service" in cmd for cmd in self.calls)) def test_normal_encrypted_login_still_autologs_in(self): - ctx = make_ctx(self.target, oem=False, encrypt=True, username="jeff") + ctx = make_ctx(self.target, defer_provisioning=False, encrypt=True, username="jeff") phases_impl.configure_login(ctx) autologin = (self.target / "etc/sddm.conf.d/autologin.conf").read_text() @@ -312,7 +312,7 @@ class ConfigureLoginOemTest(unittest.TestCase): self.assertIn("User=jeff", state) -class ConfigureSshAccessOemTest(unittest.TestCase): +class ConfigureSshAccessDeferProvisioningTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.addCleanup(self.tmp.cleanup) @@ -336,14 +336,14 @@ class ConfigureSshAccessOemTest(unittest.TestCase): run_patch.start() self.addCleanup(run_patch.stop) - def test_oem_stages_keys_for_first_boot(self): + def test_deferred_provisioning_stages_keys_for_first_boot(self): keys = Path(self.tmp.name) / "authorized_keys" keys.write_text("ssh-ed25519 AAAA rig@host\n") ctx = make_ctx(self.target, authorized_keys_path=keys) phases_impl.configure_ssh_access(ctx) - staged = self.target / "var/lib/omarchy/oem/authorized_keys" + staged = self.target / "var/lib/omarchy/provisioning/authorized_keys" self.assertEqual(staged.read_text(), "ssh-ed25519 AAAA rig@host\n") self.assertEqual(staged.stat().st_mode & 0o777, 0o600) # No user yet — nothing under /home, no chown. @@ -387,7 +387,7 @@ class CreateFactorySnapshotTest(unittest.TestCase): self.addCleanup(run_patch.stop) def ctx(self): - return make_ctx(self.target, oem=False, state_dir=self.state_dir) + return make_ctx(self.target, defer_provisioning=False, state_dir=self.state_dir) def test_snapshots_root_as_factory(self): phases_impl.create_factory_snapshot(self.ctx()) @@ -410,11 +410,11 @@ class CreateFactorySnapshotTest(unittest.TestCase): top = self.state_dir / "factory-top" factory = top / "@factory" secrets = [ - factory / "var/lib/omarchy/oem/authorized_keys", + factory / "var/lib/omarchy/provisioning/authorized_keys", factory / "etc/tailscale/authkey", - factory / "etc/omarchy/oem.key", + factory / "etc/omarchy/provisioning.key", ] - keep = factory / "var/lib/omarchy/oem/groups" + keep = factory / "var/lib/omarchy/provisioning/groups" for path in [*secrets, keep]: path.parent.mkdir(parents=True, exist_ok=True) path.write_text("secret")