Arch Linux · Hyprland / i3 · Quickshell · Neovim
| Component | Setup |
|---|---|
| Window managers | Hyprland on Wayland · i3 on X11 |
| Desktop shell | Quickshell bar, panels, notifications, and OSDs |
| Launchers | Fuzzel on Hyprland · rofi on i3 |
| Compositor | Hyprland effects · Picom for i3 |
| Editor | Neovim with lazy.nvim |
The configs favor a quiet status surface, quick keyboard workflows, and panels that reveal detail only when needed. Shared helpers keep the Hyprland and i3 setups familiar across Wayland and X11.
Quickshell · Control Center |
Quickshell · Calendar and weather |
Three launcher palettes, including a light theme. These are design previews of the launcher configuration.
![]() Slate · default |
![]() Graphite · compact |
![]() Porcelain · light |
The desktop configuration expects the repository at $HOME/dotfiles on
Arch Linux.
git clone https://github.com/lukelex/dotfiles.git "$HOME/dotfiles"
cd "$HOME/dotfiles"
./linux/install/allThe full-screen wizard explains the plan, collects component choices, and guides
SSH/GitHub, 1Password, and desktop setup. SSH access and the fonts submodule are
configured inside the wizard. Run ./linux/install/all --server for a headless
server, or choose a profile in the wizard. For a specific machine:
./linux/install/all --host <name>The installer uses the system-installed dotpkg found on PATH. Install or
update it before running the scripts; they do not download a separate version.
For local development, build the frontend and supply it explicitly:
cd "$HOME/projects/dotpkg"
mkdir -p "$HOME/.local/bin"
go build -o "$HOME/.local/bin/dotpkg-dev" ./cmd/dotpkg
cd "$HOME/dotfiles"
DOTPKG_BIN="$HOME/.local/bin/dotpkg-dev" ./linux/install/allKeep the development binary at that persistent path for login continuation. No package installation runs during a build. The launcher reports an unsupported version if the installed binary lacks the wizard.
Use arrows/Space to choose components and Enter to continue. On setup screens, Enter checks again, a runs/retries an action, o opens instructions, c copies displayed public information, d defers an optional task, and l opens logs. PgUp/PgDn scroll long screens. Password/package prompts use temporary terminal handoffs. The wizard checks real state on reruns; imperative installation stages without a check run again rather than trusting saved flags.
Pending desktop onboarding registers a terminal-based graphical-login autostart.
It is removed when setup is completed or optional tasks are explicitly deferred.
./linux/install/all --resume continues manually; r on the review screen
revisits deferred tasks. Logs and preferences live under
$XDG_STATE_HOME/dotpkg/wizard-* (default ~/.local/state/dotpkg/wizard-*).
Account handoffs and check output are not logged; package/system handoffs have
separate logs when util-linux script is available.
--plain --yes --selections choices.json enables explicit unattended operation.
Input values use DOTPKG_INPUT_SHELL, DOTPKG_INPUT_TIMEZONE, and
DOTPKG_INPUT_SSH_KEY. Package-only automation also uses --packages-only and
requires yay and non-interactive sudo access. Unresolved choices or stages
requiring human interaction fail before actions run; --yes does not select
every optional component. ./linux/install/sync remains the direct maintenance
entrypoint.
linux/packages.yaml— desktop and server package selections, config links, and services.linux/install/— sync, full setup, verification, and supporting installer steps.linux/config/— application and desktop configurations.linux/scripts/— system helpers, installed asu_*commands on desktop setups.linux/hosts/README.md— host overlay format.linux/config/fuzzel/README.md— launcher themes and shortcuts.
How installation works
sync reconciles core packages, groups, config links, and services
from the selected profile using the system-installed dotpkg binary. It symlinks
$XDG_CONFIG_HOME/dotpkg/package.yaml to the tracked linux/packages.yaml;
dotpkg stores machine-local state beside that link.
The sync wrapper always passes the repository's core manifest explicitly.
--refresh-manifest updates an existing standalone local manifest for subsequent
direct dotpkg commands, retaining a backup; it does not change which manifest
the wrapper uses. When the default manifest is already the repository symlink,
refreshing it is a no-op.
Preview the reconciliation with:
./linux/install/all --dry-runBoth all --dry-run and sync --dry-run are read-only: they do not bootstrap
tools or initialize state. When a required binary or package selection is
missing, the preview explains what cannot yet be resolved. During execution,
existing local manifests are backed up before linking to the repository version.
Pulling repository changes updates the manifest on each linked machine. The
wizard reviews declared config-link conflicts individually; backups are retained
beside their former targets in .dotpkg-backup-* directories. Direct sync
preserves conflicts unless --replace is passed. Use ./linux/install/verify
to check an existing setup.
Verification is static by default and works from a TTY. Run
./linux/install/verify --session inside the desktop to additionally check its
live session and Quickshell service. System configs and helper links preserve
conflicting paths and report an incomplete stage. Explicit --replace backs
those paths up in adjacent .dotfiles-backup-* directories before replacing
them. Backups are never pruned automatically; inspect and remove individual
backup directories explicitly when no longer needed.
Desktop installation links the dotfiles-idle and dotfiles-lock user services.
Log out and start a fresh graphical session after installing these links; the
session startup selects and starts the appropriate idle/sleep-lock owner.
Explicit sleep actions wait for the native locker's readiness acknowledgement
and refuse to sleep if locking fails. Lid-close and system-initiated sleep use
hypridle or xss-lock delay inhibitors: their delay is bounded by logind and is
not a guaranteed veto of sleep when locking fails.
Remote grill skills and OpenEffects have independent wizard stages and commands:
./linux/install/optional skills
./linux/install/optional effectsBoth accept --dry-run, --replace, and --restart-services; skills also accepts
--server. Core sync does not fetch or update either optional component.
Their manifests are in linux/optional/, with separate dotpkg ownership state
under $XDG_CONFIG_HOME/dotpkg/optional-{skills,effects}/state.yaml.
OpenEffects native packages remain owned by pacman. Errors are visible and
completed steps are retained: rerun the failed optional command to retry.
Deferring an optional stage leaves an existing installation in place.
Existing installations: run the installer wrappers before using direct
dotpkg sync or clean with the changed core manifest. They refuse to proceed
when legacy optional ownership needs migration. Explicitly run
python3 linux/install/optional-state.py to transfer those records under dotpkg's
state locks, preserving full state backups in dotpkg/optional-state-backup-*.
Destinations are saved before source
records are removed, so an interrupted transfer can be retried without losing
ownership. Conflicting destination records stop migration for inspection.
Migration also refuses any state.yaml.journal.yaml beside the core or optional
state files. Review and resolve recovery with dotpkg recover --state-file PATH
and the matching manifest before retrying, even if the journal says completed.
Do not delete a journal to bypass the check.
A read-only preview with legacy ownership reports the required migration.
The migration command performs only that local state transfer.
Do not restore just the old core state after migrating: restore the matching
optional state backups together, with no dotpkg process running. Backup filenames
start with 0- (core), 1- (skills), and 2- (effects). A missing optional backup
means that state file did not exist: move its newly created state file aside when
rolling back. Restore the old manifest declarations with the old ownership state.
No installed resources are removed by migration.
For package and resource maintenance, see dotpkg --help.
dotpkg clean --resources removes stale, unchanged managed bundles and other
stale managed resources; dotpkg recover handles an interrupted package
transaction.
If 1Password is locked or slow during shell startup, the shell continues after a
bounded attempt. Unlock 1Password, then run secrets-retry in that shell to load
the shared secret references.
Hyprland screenshots use Satty for editing. The original capture is saved to
~/screenshots first and retained if the editor is unavailable, cancelled, or
fails; clipboard failures also leave the saved capture intact.
OpenCode's global config is linked by dotpkg on desktop and server profiles.
It loads repository skills from ~/dotfiles/.opencode/skills and declares a
remote file bundle for skills/productivity/grill-me, skills/productivity/grilling, and LICENSE from
mattpocock/skills. dotpkg copies those requested paths into
~/.local/share/dotpkg/opencode-skills without running them. Invoke the skill
with @grill-me. Each linux/install/optional skills checks the upstream default
branch and updates the managed directory only if its commit or content changed;
an optional 40-character commit in resources.remote_bundles freezes it.
An unavailable upstream fails that optional stage without blocking core sync.
On existing machines, run
./linux/install/optional skills to install or update the bundle. Keep the vendored
copies until the managed installation is verified, then remove them to avoid
duplicate skill IDs. Use dotpkg v0.18.0 or newer for this bundle.
Desktop syncs install KMonad Device Manager v1.2.1 from a checksum-pinned
GitHub release (amd64). dotpkg owns the binary, bash/fish completions, man page,
license, group membership, config links, and user service. The small
linux/install/kmonad-device-manager step prepares the uinput rule and kernel
module before resource reconciliation. Fresh group membership takes effect
after logging out and back in. --packages-only skips this system setup.
The service, zsh completion (missing from the upstream release archive), and
device rules in linux/config/kmonad-device-manager come from v1.2.1. Keyboard
configs are linked from linux/config/kmonad; an existing manager env file
continues to override the default config directory.
For upgrades, update ref, archive, and sha256 in the manifest's
kmonad-device-manager entry, review the vendored service/completion/rules
against the new release, then run ./linux/install/sync --restart-services.
Release discovery is manual; dotpkg update only updates dotpkg itself.
ARM64 hosts need an overlay selecting the matching linux-arm64.tar.gz asset
and its checksum.
Migrating the old checkout-based installation: before the first managed
sync only, back up its release-file targets. dotpkg deliberately refuses to
overwrite unmanaged files, even with --replace. Run this in Bash:
backup="$HOME/.local/share/kmonad-device-manager-backup-$(date +%Y%m%d%H%M%S)"
for relative in \
.local/bin/kmonad-device-manager \
.local/share/bash-completion/completions/kmonad-device-manager \
.config/fish/completions/kmonad-device-manager.fish \
.local/share/man/man1/kmonad-device-manager.1 \
.local/share/licenses/kmonad-device-manager/LICENSE; do
if [ -e "$HOME/$relative" ] || [ -L "$HOME/$relative" ]; then
mkdir -p "$backup/$(dirname "$relative")"
mv "$HOME/$relative" "$backup/$relative"
fi
done
./linux/install/sync --replace --restart-servicesThe old checkout remains available for rollback. --replace switches its
service and zsh-completion symlinks to the dotfiles-managed versions.
Desktop syncs install checksum-pinned OpenEffects 0.1.4 native packages (x86_64)
before dotpkg links its settings and enables its user service on default.target.
The GTK preview dependency is declared in the manifest. Reactions are enabled,
the physical camera is autodetected, and capture/effects processing stops when
no application consumes the virtual camera. OpenEffects is managed by pacman,
not dotpkg's package-clean stage, until an upstream package origin is available.
To migrate an existing install, review your local manifest before running:
./linux/install/sync --refresh-manifest --replaceRefreshing backs up the local manifest; --replace can replace conflicting
config paths across the desktop profile. Chrome is the shared default browser;
its managed chrome-flags.conf enables PipeWire camera support. Fully quit and
reopen Chrome after applying it, then select OpenEffects Virtual Camera in your
calling app.
Microsoft Edge is not installed. The archqms host overlay additionally
links its flags file for an independently installed Edge. On that machine use
./linux/install/sync --host archqms --refresh-manifest --replace and fully quit
and reopen Edge after applying it.





