Niri Plus / Agent instructions

Agent setup guide

Agent-guided setup is the recommended way to install Niri Plus. Give this operational guide to your agent; it will inspect the target, adapt the setup to your choices and verify each stage. A plain Markdown version is available to share directly. The setup overview explains the desktop and its choices.

Agents: follow the numbered stages in order and open each linked recipe before applying its part.

Arch reference install target: components 0.4.3 and SwayNC 0.12.6-18 from fork release 1. It includes direct Network, Audio and Bluetooth controls, a pure-black workspace, the selected Neutral Slate appearance and the unified full-height information panel. Its authored profile uses width 448, zero margins, ungrouped notifications, a bold clock and inline Do Not Disturb control. The fresh-Arch installation and routine-task qualification is described under verified scope and in the dated receipt. The previous 0.4.1 guide at source revision 4672016 and its dated replay receipt remain historical.

Execution contract

The target is one user-authorized Arch Linux x86-64 machine with an installed OS, an ordinary sudo-capable account and working package/network access. The reference includes the browser, terminal, appearance and everyday desktop services below. OS installation, partitioning, Ubuntu/Debian, authenticated agent tools and personal fleet deployment are outside this route.

Before changing anything, establish the account/home, OS and package versions, local or SSH access, active graphical session, current display manager, network manager, portal/secret providers and configuration authority. Inspect existing files, units and startup owners. If the target is already managed by chezmoi or another tool, change that source within the user's requested scope; do not apply fresh-account copies over its deployed files.

When the user wants selected pieces or an existing setup adapted, apply only that scope and record the other stages as out of scope. Do not reset a working desktop to recreate the fresh-install tests. The commands below are individual reviewed steps, not one batch script; use your file-reading tools for inspection steps such as the PKGBUILD review.

Keep a small execution record outside the source checkout:

  • Target and reviewed source revision; existing owners and package baseline.
  • Files to merge or create, backups/preimages and recovery access.
  • User choices: actual output profiles/scales, keyboard/input languages, browser preferences, and idle/locking/suspend policy. A reference example is a choice to review, not permission to replace an existing preference.
  • Per-stage result: passed, failed, unavailable hardware, or awaiting human action, with the actual observation and any restoration still required.

Use the user's existing authorization for the agreed setup. Ask only for missing choices, credentials through their native prompt, or a disruptive transition that is not already authorized. Keep the current console/SSH recovery path working. Coordinate display-manager handover, network handover, logout and reboot with active work; do not restart an occupied SDDM session. Do not read passwords from private files or put them in chat, clipboard history, logs or captures. Browser terms/account choices and password prompts require the user's native interaction unless a disposable test account and an explicit input mechanism were supplied for that test.

Do not continue past a failed gate or substitute another compositor, portal family, secret backend or package pin to hide a failure. Diagnose and record it, restore the affected scope when needed, and return the unresolved decision. Source checks, installed files, a running service and a completed native task are different observations. Missing hardware and human-only checks remain explicit; they are not passes.

Inputs and working directory

Use a reviewed checkout of Niri Plus containing this guide, its input inventory and scripts/stage-arch-reference.py. Record its revision before starting. The input stager and authored SwayNC reference profile are checkout files, separate from the published components 0.4.3 archive and the pinned public examples. If a specific revision or source export was supplied, use that exact source; do not substitute an older component archive or a private machine's configuration.

This walkthrough starts from an installed Arch Linux x86-64 system, an ordinary sudo-capable user, working networking, and package access. It does not install Arch, partition disks, copy a private home directory, or use a display-free capture login. Keep a working console login and, when available, SSH recovery while changing the desktop. File-copy commands assume new target names. Inspect existing config and unit paths first; back them up and merge when they already contain your choices. Run the following commands from the reviewed Niri Plus checkout root and keep that path in a task-specific variable; package builds below change directories:

export NIRI_PLUS_CHECKOUT="$PWD"
test -f "$NIRI_PLUS_CHECKOUT/tools/reference/inputs.json"

The selected reference is Neutral Slate with Clear Blue focus, a pure-black workspace and Balanced+ Kitty palette, and Noto Sans/Meslo typography. Its overview retains a charcoal surround so empty black workspaces have visible boundaries. The session uses SDDM and Niri, GTK portals for file/access/settings/notification interfaces, WLR for whole-monitor capture, and the pinned SwayNC fork for the on-demand dashboard. The focused recipes retain the details for each part.

Niri Plus first-party code is covered by the repository LICENSE, and bundled design assets are indexed in assets/LICENSES.txt. The pinned SwayNC fork keeps its GPL-3.0 terms in the notification source recipe; fetched Zsh dependencies include their own third-party notices. Chrome, fonts, GTK themes, and icon themes remain governed by their upstream terms and package notices; Chrome presents its own terms on first run.

Verified scope: the numbered 0.4.3/SwayNC 0.12.6-18 route and 21 mapped routine native task groups completed through the independent fresh replay starting on 2026-11-08 from an untouched installed Arch base, with later checkpoint-copy supplements recorded separately. Cold wallet lookup and warm/cold monitor sharing passed; sharing used the documented conditional VirtIO portal candidate after a bounded stock failure. The dated qualification records exact inputs, evidence limits and the separate physical-host checklist. The VM suspend/resume check is waived at Bryan's request, with its failed observations retained. Optional extensions remain independent. Historical media and the earlier 0.4.1/package 17 receipts retain their dates. Recheck every task on a new target.

1. Prepare the account and record the starting state

Confirm that you are an ordinary user with sudo access and can recover from a console. Record the current network owner before installing or enabling another manager. The development copy used systemd-networkd and systemd-resolved; do not enable NetworkManager until its handover has a tested recovery route.

id
sudo -v
ip route
systemctl is-active systemd-networkd.service systemd-resolved.service NetworkManager.service || true
systemctl --user status || true

Apply Arch's normal full upgrade, then reboot if the transaction asks for it:

sudo pacman -Syu

Record the package baseline with pacman -Q before continuing. Arch package versions move independently of the pinned Niri Plus source inputs.

Stage 1 gate: Record the target, existing owners, recovery path, package baseline and upgrade result. Resolve an unexpected starting state before starting another network manager or display manager.

2. Stage the reviewed inputs

The first-party helper and its input inventory are checkout source files in the Niri Plus checkout containing this guide; the helper is not part of the published 0.4.3 component archive. It runs as your user and writes only into a new output tree. It verifies the immutable component archive, its manifest and all 44 payloads; the independently pinned identity; and the listed public examples from source commit 07cf4a393314c195ace7e66d162f21d3a2f73136. It then renders appearance previews and writes provenance.json with the input and output hashes. It does not install packages, write normal configuration, start services, or import capture-pilot state.

Install the staging prerequisites and set a new output path once. If the example path already exists, keep it and choose another name for STAGE:

sudo pacman -S --needed curl git less python unzip
mkdir -p "$HOME/.local/share/niri-plus"
export STAGE="$HOME/.local/share/niri-plus/arch-reference-0.4.3"
python3 "$NIRI_PLUS_CHECKOUT/scripts/stage-arch-reference.py" \
  --inputs "$NIRI_PLUS_CHECKOUT/tools/reference/inputs.json" \
  --output "$STAGE"

Set these paths for the commands below:

export SOURCE="$STAGE/source"
export BUNDLE="$STAGE/bundle"
export APPEARANCE="$STAGE/appearance"

Inspect $STAGE/provenance.json. The source checkout is used only to provide the selected public examples; the staged components and previews remain self-contained. The authored SwayNC reference.json described later is a separate reviewed checkout input and is deliberately not fetched from the pinned public snapshot.

Stage 2 gate: The new staging tree has a verified provenance record, component payloads and rendered appearance. Record its exact paths and hashes. An existing or failed staging output is not a successful new stage.

This numbered route selects only the components 0.4.3 reference pin. Manual display-away is an optional post-reference branch with its own 0.5.0 input pin; it is not part of this stage or the required reference acceptance.

3. Install the first-session packages

Install Niri, SDDM, the selected portal providers, and the basic window, terminal, and launcher tools. The SDDM package supplies its Xorg server and authentication dependencies; this reference does not require KWin.

sudo pacman -S --needed \
  niri xwayland-satellite sddm qt6-declarative qt6-svg \
  kitty rofi dolphin \
  xdg-desktop-portal xdg-desktop-portal-gtk xdg-desktop-portal-wlr \
  noto-fonts noto-fonts-emoji ttf-meslo-nerd

The pinned public Niri starter is proportional and contains no personal paths, automatic suspend policy, or session startup hooks. Inspect it and preserve any existing configuration before installing. On the fresh account, install the starter only when neither target exists:

if [ -e "$HOME/.config/niri/config.kdl" ] || [ -L "$HOME/.config/niri/config.kdl" ] ||
   [ -e "$HOME/.config/xdg-desktop-portal/niri-portals.conf" ] ||
   [ -L "$HOME/.config/xdg-desktop-portal/niri-portals.conf" ]; then
  printf '%s\n' 'Existing Niri or portal config found; inspect and merge the staged examples.'
else
  install -Dm644 "$SOURCE/examples/niri/config.kdl" "$HOME/.config/niri/config.kdl" &&
  install -Dm644 "$SOURCE/examples/portals/niri-portals.conf" \
    "$HOME/.config/xdg-desktop-portal/niri-portals.conf" &&
  niri validate --config "$HOME/.config/niri/config.kdl"
fi

The Niri starter has no PATH override. Before the first login, make later user-local launchers discoverable. Print the account's absolute home path and merge the line into a top-level Niri environment block; create that block if absent. Do not put ~ or $HOME in the KDL value. If you already customize PATH, retain its other required directories:

printf 'PATH "%s/.local/bin:/usr/local/bin:/usr/bin"\n' "$HOME"

This sets the environment of Niri-spawned apps, including Rofi, clipboard and device controls, feedback helpers and Kanshi. It does not replace the systemd user-manager environment. Validate the merged config before login:

niri validate --config "$HOME/.config/niri/config.kdl"

The portal profile selects GTK for file/access/settings/notification interfaces and WLR for whole-monitor capture. Individual-window capture remains outside this reference. If a VirtIO/VirGL guest reproduces the documented whole-monitor failure with the Arch portal package, the optional local compatibility candidate explains its reviewed build and recovery path. It is not required on a physical desktop or where the stock portal works.

Stage 3 gate: The complete Niri config validates; the intended portal configuration is installed or deliberately merged. Keep the existing login owner and recovery path until the next planned transition.

4. Verify stock SDDM password login before theming

Check that no other display manager owns the login path. Keep the distribution's SDDM/PAM configuration unchanged for this first login. From a local console, enable and start SDDM:

sudo systemctl enable --now sddm.service

Select Niri in the greeter and log in with the account password. Verify the session and user manager:

loginctl show-session "$XDG_SESSION_ID" -p Type -p State
systemctl --user is-active niri.service graphical-session.target

Log out to SDDM and log in again. If the greeter or session fails, return to the console, inspect journalctl -b -u sddm.service, and restore the previous display-manager owner. Do not add a PAM module, autologin drop-in, or theme to repair the stock login gate.

Stage 4 gate: Actual stock password login, logout to the greeter and relogin work. Record the Niri/graphical-session owners. A greeter preview or successful SSH login does not satisfy this gate.

5. Add the reference appearance and browser/files

Install the application, appearance, input, audio, and session prerequisites in one reviewed package transaction. NetworkManager and BlueZ remain installed only; do not enable them until their actual device/service handovers are ready.

sudo pacman -S --needed \
  adw-gtk-theme ark base-devel bluez bluez-utils brightnessctl \
  breeze breeze-cursors breeze-icons cliphist dconf fcitx5 \
  fcitx5-chinese-addons fcitx5-configtool fcitx5-gtk fcitx5-qt \
  fontconfig fzf python-gobject nm-connection-editor gsettings-desktop-schemas gtk3 gtk4 gtk4-layer-shell \
  gwenview kanshi kwallet kwallet-pam libadwaita libgee libnotify libsecret \
  libpulse json-glib granite7 mpv neovim networkmanager pavucontrol \
  pipewire pipewire-audio pipewire-pulse polkit polkit-kde-agent qt5-wayland qt5ct \
  qt6-wayland qt6ct shared-mime-info swayidle gtklock upower udisks2 \
  wl-clipboard wireplumber xdg-utils zoxide zsh adobe-source-han-sans-cn-fonts

base-devel and the GTK4 libraries above are for the pinned SwayNC package recipe. Do not install upstream swaync separately. Add nvidia-utils only when the host has supported NVIDIA hardware.

Use the rendered files under $APPEARANCE as appearance-only layers. The appearance recipe gives ordinary GTK3, Qt5/Qt6, KDE, Rofi, Kitty, and Gtklock targets, the fonts.json and kdeglobals-managed.json merge, a reversible GSettings profile for GTK appearance, and the Qt-only systemd user-service environment profile. The pavucontrol GTK4 dark-surface check passed through the selected GSettings preference with GTK_THEME unset. The notification recipe owns the selected SwayNC stylesheet target. Merge the Niri fragment's layout settings into the starter's existing layout block and add its separate top-level overview block, keep its proportional columns and bindings, and validate the complete file after each change. Use the login recipe to preview the pinned greeter theme and install it only after the stock password login passed. Use the matching renderer outputs in $APPEARANCE; do not download or copy a second palette snapshot.

The transaction above includes the file tools. Follow the files recipe to install the Neovim desktop entry only when absent and apply the staged MIME defaults with xdg-mime, which leaves unrelated associations intact.

The reference browser is Chrome. Its Arch recipe is pinned at commit f0071e0692f4545bcb3e0881ea60c031a9f99343 for version 156.0.8078.47:

CHROME_BUILD=$(mktemp -d "$HOME/.local/share/niri-plus/chrome-build.XXXXXX")
git clone https://aur.archlinux.org/google-chrome.git "$CHROME_BUILD"
git -C "$CHROME_BUILD" checkout --detach f0071e0692f4545bcb3e0881ea60c031a9f99343
test "$(git -C "$CHROME_BUILD" rev-parse HEAD)" = f0071e0692f4545bcb3e0881ea60c031a9f99343
less "$CHROME_BUILD/PKGBUILD"
cd "$CHROME_BUILD"
makepkg --verifysource
makepkg --syncdeps --rmdeps

Inspect the exact generated google-chrome package with pacman -Qip and install that file with sudo pacman -U. Record the recipe revision and installed package version. Launch Chrome normally from Mod+D for its first run. Review its terms and browser-default/telemetry choices yourself, and leave the browser signed out. Do not pass first-run suppression flags or reuse an isolated lab profile. Use disposable PDF, image, audio/video, and archive samples to check defaults, browser download/open/upload, and safe archive extraction. After stage 7 supplies authorization, check UDisks mounting with a new explicitly authorized disposable disk for read/write/unmount.

Stage 5 gate: The selected GTK, Qt/KDE, Rofi, Kitty and greeter layers come from the same staged identity. Inspect real application surfaces and validate Niri after merging. Record Chrome's source/package, the user's first-run choices and actual file-default tasks; keep account state local.

6. Install the selected notification and clipboard paths

The full dashboard requires the pinned swaync-bryan fork. Follow the notification recipe to review and build its exact recipe/source tuple as an ordinary user, inspect the package, and install the replacement transaction. The candidate dashboard config is a small authored file in this source checkout; verify its recorded hash before copying it. Use $APPEARANCE/swaync.css for the selected stylesheet.

For text and image clipboard history, use the generated minimal-Niri service and its paired 200-item Cliphist config. The clipboard recipe has the exact targets and niri.service wants. Do not combine it with the separate portable collector unit.

Network, Audio and Bluetooth use direct Rofi controls from the verified bundle. Session keeps its existing helper. Install the complete controls library, wrappers and derived appearance before installing their launcher entries. NetworkManager, the audio server and BlueZ retain device ownership; opening a menu does not start a missing provider or change a connection.

The Niri starter binds Mod+D to the normal Rofi application list. Preserve it or merge this line into an existing Niri binds block when it is absent:

Mod+D { spawn "rofi" "-show" "drun"; }

Use Network after NetworkManager owns the link. The network owner handover is a narrow conditional example for the exact cloud-init/networkd starting state; a system already owned by NetworkManager skips it. Package installation does not start BlueZ. Start the packaged bluetooth.service only when an adapter is present and ready; do not run a second daemon. The Bluetooth menu reports absent hardware or an unavailable provider without silently enabling it. Retain nm-connection-editor, pavucontrol, nmtui and bluetoothctl as native advanced settings and recovery tools.

PipeWire and WirePlumber keep their packaged socket/session ownership. If their packages were installed after this user manager was already running, start the standard units for the current session before checking audio:

systemctl --user start pipewire.socket pipewire-pulse.socket wireplumber.service
systemctl --user is-active pipewire.socket pipewire-pulse.socket wireplumber.service
wpctl status

Install the shared launcher helper and entries:

install -Dm755 "$SOURCE/examples/desktop/desktop-session-ready" \
  "$HOME/.local/bin/desktop-session-ready"
install -Dm755 "$SOURCE/examples/desktop/desktop-session-menu" \
  "$HOME/.local/bin/desktop-session-menu"
install -d -m755 "$HOME/.local/bin" "$HOME/.local/lib/niri_plus_controls"
for helper in desktop-controls desktop-network-menu desktop-audio-menu desktop-bluetooth-menu; do
  install -m755 "$BUNDLE/components/desktop/bin/$helper" "$HOME/.local/bin/$helper"
done
for module in "$BUNDLE"/components/desktop/lib/niri_plus_controls/*.py; do
  install -m644 "$module" "$HOME/.local/lib/niri_plus_controls/"
done
install -Dm644 "$APPEARANCE/niri-plus-controls.rasi" \
  "$HOME/.config/rofi/niri-plus-controls.rasi"
install -Dm644 "$APPEARANCE/controls-appearance.json" \
  "$HOME/.config/niri-plus/controls-appearance.json"
python3 -c 'import gi; gi.require_version("NM", "1.0"); gi.require_version("Secret", "1"); from gi.repository import NM, Secret, Gio'
install -Dm644 "$BUNDLE/components/desktop/share/applications/niri-plus-network.desktop" \
  "$HOME/.local/share/applications/niri-plus-network.desktop"
install -Dm644 "$BUNDLE/components/desktop/share/applications/niri-plus-bluetooth.desktop" \
  "$HOME/.local/share/applications/niri-plus-bluetooth.desktop"
install -Dm644 "$BUNDLE/components/desktop/share/applications/niri-plus-audio.desktop" \
  "$HOME/.local/share/applications/niri-plus-audio.desktop"
install -Dm644 "$SOURCE/examples/desktop/niri-plus-session.desktop" \
  "$HOME/.local/share/applications/niri-plus-session.desktop"

Check the graphical session's PATH resolves all four installed wrappers and that each menu opens through the application list. In each landing list, Tab/Shift+Tab cycle Open and Details; Enter runs the visible action. Back restores the filter and selection, and Escape/Ctrl+G closes the menu. The controls recipe records credentials, pairing, advanced-tool and recovery behavior. A VM without wireless hardware verifies its unavailable state, not physical Wi-Fi or Bluetooth operation.

The desktop-utility recipe records Session and the older recovery entry points. Use the hardware-feedback recipe to install both helpers from SOURCE and merge all six hardware-key binds from the verified fragment into the existing Niri binds block.

Stage 6 gate: One notification owner, two paired clipboard collectors, and the intended launcher entries are installed. Exercise the panel, exact text/image restore and Delete/Cancel/Clear. Open each actual controls entry; check device state, named actions, Details, Back and cancellation. Exercise available audio output/input separately and record absent Wi-Fi/Bluetooth. Check feedback only on available devices. Native providers retain ownership.

7. Wire wallet, policy, and authorization services

Keep one owner for each function. Choose and record the power policy. Reuse the readiness helper installed in section 6, and install the reviewed units into ordinary user paths:

install -Dm755 "$SOURCE/examples/session/desktop-power" \
  "$HOME/.local/bin/desktop-power"
install -Dm644 "$SOURCE/examples/session/profiles/minimal-niri/desktop-lock.service" \
  "$HOME/.config/systemd/user/desktop-lock.service"
install -Dm644 "$SOURCE/examples/session/desktop-idle.service" \
  "$HOME/.config/systemd/user/desktop-idle.service"
install -Dm644 "$SOURCE/examples/session/desktop-battery.service" \
  "$HOME/.config/systemd/user/desktop-battery.service"
install -Dm644 "$SOURCE/examples/session/polkit-session.conf" \
  "$HOME/.config/systemd/user/plasma-polkit-agent.service.d/session.conf"

On the development copy, /etc/pam.d/sddm already contained the package's optional pam_kwallet5 auth/session entries. The KWallet PAM package and Niri unit wiring were added after the initial stock login, so only a later fresh password login tested their handshake. Keep the installed PAM file unchanged; inspect it and the kwallet-pam package on a new system rather than copying another machine's file. Wire the packaged PAM initialization unit to the Niri graphical session with this user drop-in and one Niri wants link:

mkdir -p "$HOME/.config/systemd/user/plasma-kwallet-pam.service.d"
cat > "$HOME/.config/systemd/user/plasma-kwallet-pam.service.d/session.conf" <<'EOF'
[Unit]
After=graphical-session.target
Requisite=graphical-session.target
EOF
systemctl --user add-wants niri.service plasma-kwallet-pam.service

Merge this preference into the existing Niri portal configuration so the KWallet backend owns the Secret portal for this reference setup:

[preferred]
org.freedesktop.impl.portal.Secret=kwallet;

Finish the appearance and input steps in section 8 before testing the wallet. The KWallet PAM package and packaged Fcitx5 autostart both need a fresh login after installation. Do not run secret-tool from the earlier stock-login session.

Before installing the idle service, review $SOURCE/examples/session/power.toml.example, choose values, and save the full policy to ~/.config/desktop-session/power.toml. Do not start the policy helper with a missing or unreviewed policy. Verify the units and add only the idle, battery, polkit, and KWallet PAM services to Niri; the manual lock unit stays on-demand:

systemctl --user daemon-reload
systemd-analyze --user verify \
  "$HOME/.config/systemd/user/desktop-lock.service" \
  "$HOME/.config/systemd/user/desktop-idle.service" \
  "$HOME/.config/systemd/user/desktop-battery.service" \
  plasma-polkit-agent.service plasma-kwallet-pam.service
if ! systemctl is-active --quiet polkit-agent-helper.socket; then
  sudo systemctl start polkit-agent-helper.socket
fi
systemctl --user add-wants niri.service \
  desktop-idle.service desktop-battery.service plasma-polkit-agent.service \
  plasma-kwallet-pam.service
systemctl --user daemon-reload
systemctl --user start \
  desktop-idle.service desktop-battery.service plasma-polkit-agent.service

These are candidate Niri session wires; verify one instance at login and clean teardown at logout using the session recipe. The packaged PipeWire/WirePlumber user services retain their native activation; do not add Niri spawns or duplicate units for them. Fcitx5's packaged XDG autostart and the BlueZ daemon keep their own owners. Do not start a second SwayNC, clipboard, audio, or input-method process from Niri.

When polkit was installed after sockets.target had started, the static helper socket may still be inactive for this boot. Starting the packaged polkit-agent-helper.socket activates its native route; it is normally socket activated after boot, so do not enable the static unit or change its packaged permissions. Run a graphical pkexec /usr/bin/true check and authenticate to confirm the prompt and allow path. Cancelling denies the action; the fresh-replay test returned 127 on cancel and 0 after authentication, with the packaged polkit files unchanged.

The KWallet/Secret Service path, manual Gtklock password check, SDDM-session lock request, idle restart with an existing locker, polkit prompt/allow path, native Chrome/Kitty input composition, and guest playback through its virtual HDA device passed the fresh-guide replay. Physical/SPICE audibility and microphone signal are not established. Restart cancellation preserved the same boot ID; confirmed Restart rebooted the guest, and confirmed Shut down powered it off. A later full power-on, logout and relogin restored the expected owners.

Stage 7 gate: Units verify, the full power policy is explicitly chosen, and startup ownership has no duplicates. Test real polkit cancel/allow and manual lock rejection/unlock. Keep the locker alive during an idle reload. Complete stage 8 and its fresh password-login gate before testing the wallet.

8. Add display, input, audio, and shell workflows

Kanshi has no packaged user unit in the current Arch snapshot. Adapt $SOURCE/examples/displays/kanshi.conf to the actual outputs, then use the single startup owner described in the display recipe. The installed Fcitx5 package has its own XDG autostart desktop entry. Niri's packaged session starts xdg-desktop-autostart.target; verify app-org.fcitx.Fcitx5@autostart.service and one fcitx5 process after the fresh login described below. Do not add a Niri fcitx5 -d spawn or a second user service. The input recipe supplies a guarded fresh-account profile for keyboard-us, Pinyin, US layout, and Super+Space. Use fcitx5-configtool when modifying an existing profile.

For direct Niri-launched applications, merge these values into the existing Niri environment block and validate the complete config:

environment {
    QT_IM_MODULE "fcitx"
    XMODIFIERS "@im=fcitx"
    SDL_IM_MODULE "fcitx"
}

QT_IM_MODULE is for Qt clients using Fcitx under Niri; XMODIFIERS is for XIM clients under X11/XWayland; retain SDL_IM_MODULE for SDL2 clients. Leave GTK_IM_MODULE unset globally. Niri's environment block does not change the systemd user-manager environment; the appearance recipe's environment.d file covers user services only. See the Fcitx Wayland guide for toolkit-specific behavior.

Keep the user-local PATH configured in stage 3 when merging these environment values; do not replace the existing block.

Fresh-guide checks passed for native composition in Chrome 156 launched with --ozone-platform=wayland only and Kitty 0.49.2 using native Wayland text input. Preedit, cancellation, candidate selection, commit, and Super+Space Latin switching worked; the candidate popup followed fields at scale 1.5. See the receipt for exact committed text and versions. Do not add GTK_IM_MODULE, --enable-wayland-ime, or a forced text-input version to these tested routes.

Retain the packaged audio units started in stage 6; verify them again after the fresh login before the playback and microphone checks.

On the fresh-guide replay, an attached virtual ICH9 HDA device produced the real PipeWire sink alsa_output.pci-0000_00_1b.0.analog-stereo; a three-second test tone reached the ALSA PCM (RUNNING) and its monitor. This checks the guest playback path only. It does not establish physical or SPICE audibility or microphone signal. Do not substitute a dummy sink for playback acceptance. The hardware-feedback recipe verifies volume and notification controls. Check brightness and radio features only when the corresponding hardware exists.

Fresh password-login gate for wallet and autostart

After completing the appearance, session, and input changes, log out to SDDM and password-login again. This is the first login after installing kwallet-pam and Fcitx5, so it exercises the real PAM handoff and starts the packaged input method autostart. Check one Fcitx5 process and the KWallet PAM initialization result. The initialization process exits after its handoff, so its unit can be inactive afterward. Confirm a start during this fresh login, Result=success and ExecMainStatus=0; then verify the provider and store/read a disposable secret without another password prompt. Keep it in the wallet until after the cold-login check:

systemctl --user show plasma-kwallet-pam.service \
  -p Type -p RemainAfterExit -p ActiveState -p ExecMainStartTimestamp \
  -p Result -p ExecMainStatus
test -n "$(systemctl --user show plasma-kwallet-pam.service -p ExecMainStartTimestamp --value)"
test "$(systemctl --user show plasma-kwallet-pam.service -p Result --value)" = success
test "$(systemctl --user show plasma-kwallet-pam.service -p ExecMainStatus --value)" = 0
systemctl --user status app-org.fcitx.Fcitx5@autostart.service
pgrep -ax fcitx5
busctl --user status org.freedesktop.secrets
busctl --user status org.freedesktop.impl.portal.desktop.kwallet
secret-tool store --label='Niri Plus disposable check' service niri-plus-reference account disposable
secret-tool lookup service niri-plus-reference account disposable

Reboot and password-login once more. Check the secret again before clearing it:

secret-tool lookup service niri-plus-reference account disposable
secret-tool clear service niri-plus-reference account disposable

The fresh-guide replay opened the wallet without a second password prompt and read the disposable secret after password login following reboot. A later cold power-on and final relogin also retained the native provider and passed a new disposable store/read/clear check. Keep PAM unchanged while testing its normal package handoff.

Stage the four pinned shell dependency trees manually from the verified examples. Choose a new leaf, then follow the terminal recipe to add the source shim and Kitty shell choice:

ZSH_BUNDLE="$HOME/.local/share/niri-plus/zsh-neutral-slate"
python3 "$SOURCE/examples/terminal/fetch.py" "$ZSH_BUNDLE"

This fetcher requires network access and runs only when invoked; it is not a shell-startup installer. Do not copy prompt caches from another home. On the fresh-guide check, the first Kitty Git prompt fetched the pinned Powerlevel10k gitstatusd binary, Tab completed a partial directory path, and Up/Enter repeated the prior command. This first prompt needs network access; it is not an offline startup guarantee.

Stage 8 gate: One Kanshi and one packaged Fcitx owner; supported output change restored; actual composition in Chrome and Kitty; real audio sink and signal where available; fresh-login wallet lookup; first terminal prompt, history and completion. Do not use pasted Unicode as IME evidence or a dummy sink as hardware playback evidence.

9. Replay the task checklist and recover cleanly

Before claiming the reference complete, run the tasks in the login, files, notifications, clipboard, session, display, input, and hardware-feedback recipes. Reboot to SDDM, password-login, test core tasks, log out, and log in again. Record package versions and whether each required service has exactly one owner. Resolve a failed gate in its owning source and record the change before continuing. Repeat affected commands and native tasks from saved preimages or a fresh working copy; restage changed component/identity pins before testing. Report every guide amendment and which gates were repeated.

Keep backups of any native files changed and exact preimages for privileged SDDM files. To back out a user service, stop only the unit introduced, remove only its exact niri.service.wants link, and restore its saved file. Keep the console path working until SDDM login, lock/unlock, and session teardown pass.

Completion report

Use this checklist with the linked recipes. Report the observed result for each row; do not silently drop tasks that could not be exercised.

Area Required observation
Login and secrets Stock login before theming, themed login, actual polkit cancel/allow, disposable wallet lookup after another password login; clear the disposable secret.
Session and power Lock rejects/accepts input, repeated lock retains one locker, idle reload preserves it, the graphical session's loginctl lock request works; restore any short test timer.
Displays and input Actual supported output/scale change and restoration; preedit, cancel, candidate selection and committed text in Chrome and Kitty.
Everyday desktop Available panel metrics; Rofi entries; exact text/image clipboard restore, Delete and Cancel-first Clear; volume/mute restoration with no history pollution.
Files and devices PDF/image/audio/video/archive defaults, archive byte comparison, browser download/open/upload via GTK picker; available radio/storage/backlight tasks recorded separately. Format only a new explicitly authorized disposable disk.
Audio and sharing Real sink/monitor signal where available; whole-monitor share advances frames, Stop releases tracks/nodes; repeat sharing after a full cold start. Use the conditional VirtIO branch only for its demonstrated guest failure.
Lifecycle Planned reboot, logout teardown and password relogin restore exactly the intended owners. Keep recovery available until this passes.

At handoff, report the target and source revision, installed versions and provenance, changed files/service owners, user choices, per-task observations, remaining human/hardware work, compatibility branches, backups/recovery and final running/stopped state. State whether the requested scope is complete or which required task is unresolved. Do not push, publish or extend to other hosts unless that was part of the user's request.

The current qualification records the numbered 0.4.3/SwayNC18 installation, 21 mapped routine tasks and the user-waived VM suspend/resume check, alongside its conditional VirtIO branch. The earlier sync retains scoped package 17 checks and their native captures. The dated 0.4.1 installation receipt retains the earlier fresh-base run, its guide amendments and repeated gates. Physical-host and human acceptance remain separate.

Optional post-reference branch: manual display-away

After completing the numbered 0.4.3 reference, you may separately select the manual away companion. Read the away-mode recipe for its 0.5.0 pin, staged-file review, install paths, optional key binding, status and rollback. It retains desktop-power/swayidle as the automatic idle and suspend owner; do not connect automatic resume to the manual away latch.

Record this extension separately from the required reference checklist:

Optional extension Completion record
Manual display-away, if selected State not selected, or record the reviewed 0.5.0 provenance, installed helper/unit, exact Niri include or merge, selected input backend, away/resume/stop restoration result and rollback preimages. Keep physical input and panel acceptance separate.

The integration record summarizes the current release split and dated host evidence. It does not extend or replace the preserved 0.4.1 VM and media receipts or the separate current 0.4.3 replay.

Optional post-reference branch: unified session screen

Published components 0.9.2 offer the native session screen through desktop-session-menu --screen. Treat it as an independently reviewed extension after the reference. Use the components-v0.9.2 checkout's generated helpers and examples/session/profiles/minimal-niri/session-screen.kdl, preserving existing target and binding preimages. Do not take these files from the frozen 0.4.3 $SOURCE, silently select the full new bundle, or add the screen to the numbered reference's completion claim.

Record the exact release revision and helper/profile selection; GTK and GtkLayerShell prerequisites; the existing locker, Away and suspend-policy owners; the installed Ctrl+Alt+Delete override; cancellation/refusal results; and rollback paths. Lock must establish the lock then request display-off; Display off must add no lock, on every machine. All six choices retain the countdown as their confirmation: selection starts the timer and expiry sends the request without another activation. Keep automatic policy and ordinary inhibitors unchanged. Do not permit the screen above the real locker.

Source tests and private native GUI checks are distinct from actual lock readiness, installed-consumer and physical panel/wake acceptance. A consumer's managed source owns final machine admission and deployment; do not run a broad apply to adopt the screen.

Optional post-reference branch: Close All

Published components 0.10.0 contain the independent Close All utility and examples/desktop/profiles/minimal-niri/close-all.kdl. It requests ordinary closes across all workspaces and outputs from one captured window-ID list. Apps retain their save/cancel dialogs; this is not logout or a process kill.

Verify the immutable published tuple before managed adoption. Select only niri-close-all and the optional profile from that verified archive, preserving the existing component and policy pins. Merge Mod+Shift+Q with repeat=false and keep Mod+Q unchanged. Preserve the helper/binding preimages and record source, archive/manifest hashes, installed hashes/modes and scoped convergence. This extension does not join the numbered reference's completion claim.

Use fixture command traces or controlled windows for acceptance. A dry run can inspect the one ID snapshot without closing windows; ordinary live work must never serve as a destructive test fixture. Follow the recipe's JSON/exit-status contract and do not infer actual application exit from a successful request.

Focused recipes

Setup overview →Read on GitHub ↗