X Linux
Documentation menu

Overview/X Linux (distro)

Text installer

X is installed from the live ISO using a text installer. There is no graphical installer (Calamares was removed). Everything below lives under airootfs/root/x-installer/ in this repository and is shipped in the live environment at /root/x-installer/.

Entry points and when the installer runs

  • The live environment auto-logs in as root on TTY1 (agetty autologin) with zsh.

  • /root/.zlogin first runs /root/.automated_script.sh (the official archiso script= mechanism) if present, and then launches the installer on TTY1, unless the kernel cmdline contains script= or xauto=1.

  • The entry point is installer.sh (/root/x-installer/installer.sh).

  • A shortcut is available in the live shell:

    xinstall
    

    xinstall (/usr/local/bin/xinstall) simply execs installer.sh. If you are dropped to a plain shell instead of the installer, run xinstall or bash /root/x-installer/installer.sh.

Installer layout

/root/x-installer/
|-- installer.sh        # entry point (configurator + install)
|-- configurator.sh     # interactive configuration, writes a JSON plan
|-- install.sh          # performs the actual installation
|-- autoinstall.sh      # unattended entry (xauto=1 + cidata disk)
|-- ui.sh               # UI helpers: gum with plain-prompt fallback
|-- packages.x86_64     # default manifest for the "full" package profile
`-- packages/           # offline x-scripts payload (*.pkg.tar.zst)

A systemd unit, x-autoinstall.service, is enabled in the live image (airootfs/etc/systemd/system/) and drives the unattended path. See Autoinstall below.

Interactive flow

installer.sh runs the configurator and, if it succeeds, the installer.

  1. configurator.sh collects the options below and writes /tmp/x-install.json (mode 600).
  2. install.sh reads that JSON, validates it, and performs the installation.

With X_DRY=1, the plan is shown without touching the disk (see Environment variables).

Configurator options (configurator.sh)

The UI uses gum (choose, input, confirm) when available and falls back to plain text prompts otherwise (ui.sh).

StepOptionsStored value
Diskany block device of type disk (from lsblk)disk (e.g. /dev/sda)
System languageEnglish, Español, Deutsch, Françaislanguage + locale
Keyboard layoutus, es, de, fr, uk, latam, br-abnt2keyboard
TimezoneUTC, Europe/Madrid, Europe/London, Europe/Berlin, America/Mexico_City, America/Argentina/Buenos_Aires, America/Los_Angeles, Asia/Tokyotimezone
Hostnamefree text (default x)hostname
Usernamefree textusername
User passwordfree text (repeated)password
Package profileFull (all packages) / Core (minimal system)profile (full/core)
BootloaderGRUB (BIOS + UEFI) / systemd-boot (UEFI only)bootloader (grub/systemd-boot)
Root encryption (LUKS)yes/noencryption (yes/no)
LUKS passphrasereuse user password or dedicatedluks_password
Install Hyprland setupyes/no (requires network)hyprland (yes/no)

Language-to-locale mapping used by the configurator:

Languagelanguagelocale
Englishenen_US.UTF-8
Españoleses_ES.UTF-8
Deutschdede_DE.UTF-8
Françaisfrfr_FR.UTF-8

Validation rules: hostname must match ^[a-zA-Z0-9][a-zA-Z0-9-]{0,62}$; username ^[a-z_][a-z0-9_-]{0,31}$; passwords/passphrases cannot contain " or \.

Before writing the config, the configurator asks for final confirmation that everything on the selected disk will be erased. The resulting JSON looks like:

{"disk":"/dev/sda","hostname":"x","username":"x","password":"secret","language":"en","locale":"en_US.UTF-8","keyboard":"us","timezone":"UTC","profile":"full","bootloader":"grub","encryption":"no","luks_password":"","hyprland":"no"}

The JSON is written to the path in X_CONFIG_OUT (default /tmp/x-install.json). Only disk, hostname, username, and password are required; the remaining keys have sensible defaults when absent.

Installation steps (install.sh)

  1. Parse and validate the JSON (disk, hostname, username), require root and a real block device.
  2. Partition with GPT (sgdisk --zap-all first):
    • grub: 1 MiB bios_grub partition, 512 MiB EFI partition, rest = root.
    • systemd-boot: 512 MiB EFI partition, rest = root.
  3. LUKS (if encryption=yes): cryptsetup luksFormat --type luks2 on the root partition (passphrase from luks_password, falling back to the user password) and open it as /dev/mapper/xroot.
  4. Format and mount: the EFI partition as FAT32 (mkfs.vfat -F32) mounted at /mnt/boot, the root (or LUKS mapping) as btrfs mounted at /mnt.
  5. Package set:
    • Base set: base base-devel linux linux-firmware sudo networkmanager openssh git jq x-release kitty pipewire pipewire-pulse pipewire-alsa wireplumber alsa-utils sddm, plus grub efibootmgr for GRUB and cryptsetup for LUKS.
    • full profile: adds every package in the manifest pointed to by X_PKGLIST (default /root/x-installer/packages.x86_64).
    • core profile: adds only vim zsh.
  6. Wait for network (DNS check against geo.mirror.pkgbuild.com, up to ~120 s) and run pacstrap -K /mnt <pkgs> from the official mirrors plus the [x] repository.
  7. Install x-scripts offline: the payload packages/x-scripts-*.pkg.tar.zst present in the live environment is copied into the target and installed with pacman -U inside the chroot.
  8. Base configuration: genfstab, timezone symlink, locale.gen + /etc/locale.conf, KEYMAP in /etc/vconsole.conf, hostname, copy of the working mirrorlist, and the [x] repository appended to the target's pacman.conf if missing.
  9. User: create the user (member of wheel, login shell bash), set the password with chpasswd, and enable %wheel in sudoers.
  10. Provisioning with the x CLI from the x-scripts package:
    • system phases as root: X_HW_AUTO=0 x setup;
    • user phases as the new user: X_HYPRLAND=0 X_HW_AUTO=0 x setup --user.
    • PipeWire/Pulse/WirePlumber are enabled for all users; the Hyprland setup is deferred to a later step/point (not run here when hyprland=no).
  11. Hyprland setup (only if hyprland=yes): a temporary passwordless-sudo drop-in is created, and /usr/share/x/tools/hyprland-install.sh runs as the target user. The drop-in is removed afterwards. The tool prefers the offline config snapshot shipped in the package (/usr/share/x/config); it only clones the external xscriptor-colors/hyprland repo (branch main) as a fallback, and never configures NVIDIA (that is handled by the system hardware phase).
  12. Initramfs (LUKS only): replace HOOKS in mkinitcpio.conf to include the encrypt hook and rebuild with mkinitcpio -P.
  13. Branding: x-release-apply (from x-release) is run before the bootloader step so a LUKS kernel cmdline written afterwards is not overwritten.
  14. Bootloader:
    • grub: grub-install for x86_64-efi (removable) and i386-pc (booting from the whole disk), then grub-mkconfig. For LUKS the GRUB_CMDLINE_LINUX is set to cryptdevice=UUID=<luks-uuid>:xroot root=/dev/mapper/xroot rw.
    • systemd-boot: bootctl --esp-path=/boot install, a BOOTX64.EFI removable fallback if needed, and a loader entry X Linux (UEFI only) with the matching root= or cryptdevice= cmdline.
  15. Cleanup: on exit, mounts are unmounted, the LUKS mapping is closed if open, and the install JSON is removed.

A message tells you the installation is complete; reboot and remove the installation medium.

Autoinstall

The installer supports unattended installation from the live ISO:

  • The kernel cmdline must contain xauto=1.
  • A storage device labeled cidata must exist and contain an x-install.json file (for example an extra virtual disk in QEMU).

x-autoinstall.service (enabled in the live image) runs autoinstall.sh, which:

  1. Skips immediately if xauto=1 is not present on the cmdline.
  2. Looks up the device by label (blkid -L cidata); skips if absent.
  3. Mounts it read-only at /run/cidata.
  4. Runs install.sh with X_INSTALL_JSON pointing at /run/cidata/x-install.json.
  5. Writes the log to /tmp/x-install.log and echoes it to the serial console / console, then unmounts.

The JSON for an unattended run only requires the base keys, for example:

{"disk":"/dev/vda","hostname":"x-vm","username":"x","password":"secret","profile":"core","bootloader":"grub","encryption":"no","hyprland":"no"}

See Testing in a VM for an example cidata disk.

The other automation mechanism is the official archiso script= cmdline (/root/.automated_script.sh downloads or copies a script and executes it). When script= or xauto=1 is present, the interactive installer is not launched.

Kernel cmdline reference

ParameterEffect
script=<url or path>Runs the official archiso automation script; interactive installer is skipped.
xauto=1Enables the unattended autoinstall (needs a cidata disk with x-install.json).
accessibility=Sets single-line zle (screen-reader friendly TTY).

Environment variables

VariableDefaultScopeEffect
X_SKIP_INSTALLERunsetlive1 makes installer.sh print "skipped" and exit.
X_DRY0live1 shows the plan only; nothing is written or erased.
X_CONFIG_OUT/tmp/x-install.jsonconfiguratorWhere the config JSON is written.
X_INSTALL_JSON/tmp/x-install.jsoninstallerJSON config consumed by install.sh.
X_PKGLIST/root/x-installer/packages.x86_64installerPackage manifest used by the full profile.
X_HYPRLANDpayload default 1payload (x setup)Set to 0 by the installer to defer the Hyprland setup.
X_HW_AUTOpayload default 1payload (x setup)Set to 0 during install to disable hardware auto-detection.

Notes:

  • X_DRY=1 in installer.sh ensures a JSON exists (with a placeholder if needed) and runs install.sh, which prints the install plan and exits without touching the disk. The configurator, when run with X_DRY=1, writes the JSON without the password and stops before the erase confirmation.
  • X_HYPRLAND/X_HW_AUTO belong to the x-scripts payload; the installer sets them when calling x setup.

Requirements and caveats

  • Installation requires network access: pacstrap pulls from the official Arch mirrors and the [x] repository. An offline mirror bundled in the ISO is pending (see the workspace ROADMAP).
  • The target disk is completely erased.
  • systemd-boot is UEFI only; GRUB writes both a BIOS (with the bios_grub partition) and a UEFI (removable) path, so either boot mode works.
  • LUKS uses LUKS2 with the legacy encrypt initramfs hook.

Edit this page on GitHub