ttyforce
Text user interface for installing Town OS. Presented during disk provisioning, it handles network configuration and disk setup through an nmtui-style interface.
Usage
# Detect hardware and print the manifest to stdout
# Detect hardware, save manifest to a file
# Print manifest from an existing hardware file (no auto-detection)
# Run a scripted fixture scenario and print the resulting operations
# Detect real hardware, run the TUI with a mock executor,
# and print the operations that would be performed (dry run)
# Same dry run, but load hardware from a file
# Save the dry-run operations manifest to a file
# Detect hardware and launch the real installer
# Launch the real installer with hardware from a file (mock executor)
Subcommands
| Subcommand | Description |
|---|---|
detect |
Detect hardware and print the hardware manifest. With --fixture, runs a scripted scenario and prints the resulting operations manifest instead. |
output |
Detect real hardware (or load via -i), run the full TUI with a mock executor so no real changes are made, then print the operations that would have been performed. |
run |
Detect hardware (or load via -i) and launch the real installer. Uses the real executor when auto-detecting, mock executor when loading from file. |
Global flags
| Flag | Description |
|---|---|
-i, --input <FILE> |
Load hardware from a manifest file instead of auto-detecting. |
-o, --output <FILE> |
Write output to a file instead of stdout. |
How it works
Network
The installer prioritizes getting online with minimal user interaction:
- If a wired connection with link+carrier is already up, it selects it and advances directly to disk setup — no probing or DHCP reconfiguration
- If ethernet has link but no carrier, it brings the interface up step by step (enable, check link, DHCP, IP check, connectivity checks)
- If ethernet is dead or absent, it falls back to wifi
- Wifi presents a scannable network list with signal strength and security info
- Supports WPA2/WPA3 password entry and QR code configuration
Disks
Disks are automatically grouped by make and model. The filesystem is always Btrfs. RAID options are presented based on disk count:
- 1 disk — single drive
- 2 disks — RAID1 (Btrfs mirror)
- 3+ disks — RAID5 (Btrfs striped with parity)
The installation target mount point defaults to /town-os.
Final screen
After installation completes (or is aborted), the final screen offers three choices:
- Reboot — restart the machine into the new system
- Exit — return to the shell
- Power Off — shut down
Hardware detection
Detection uses systemd dbus interfaces with sysfs/command fallbacks. Negative results from networkd are never trusted on their own — the installer always falls through to direct system checks (sysfs carrier, ip addr show, ip route, etc.):
- Network interfaces — systemd-networkd (
org.freedesktop.network1) for link/carrier state, wpa_supplicant dbus for wifi scanning, sysfs andipcommand as fallbacks - Disks — UDisks2 (
org.freedesktop.UDisks2) for block device enumeration and drive metadata - DNS — systemd-resolved (
org.freedesktop.resolve1) for name resolution, withdig/getentfallback
Testing
# Unit + fixture + scenario + playbook + CLI tests (includes lint)
# Integration tests in a container (requires podman, uses sudo if needed)
Fixtures
Hardware manifests in fixtures/hardware/ define simulated hardware configurations:
ethernet_4disk_same— ethernet + 4 identical disksethernet_1disk— ethernet + 1 diskwifi_1disk— wifi only + 1 diskwifi_crowded_1disk— crowded wifi neighborhoodwifi_ethernet_*— both interfaces presentwifi_dead_ethernet_*— dead ethernet, wifi availablemixed_drives_*— workstation/server/homelab with mixed drive vendors
Use with detect or output via -i:
Scenarios
Scripted test cases in fixtures/scenarios/ feed inputs and mock responses to the state machine non-interactively:
Playbooks
Playbooks in fixtures/playbooks/ extend scenarios with assertions — expected screen transitions, operation sequences, and final state. These are verified by cargo test --test playbook_tests.
License
MIT