feat: controller setup
This commit is contained in:
parent
37b22758bd
commit
41f63c9a3d
19 changed files with 2128 additions and 247 deletions
|
|
@ -4,7 +4,7 @@ Noctalia 5.0.1 replaces Waybar, Walker/Elephant, Mako, swaylock/hyprlock,
|
|||
swayidle/hypridle, swaybg/hyprpaper, meeting guard, and `theme-switch`.
|
||||
Their modules, launcher/locker/wallpaper flake inputs, Walker cache, and
|
||||
Waybar-specific passwordless WireGuard polling rules are removed.
|
||||
Framework, Phantom and Steam Deck use Niri. The live ISO still uses Hyprland.
|
||||
Framework, Phantom and Steam Deck use Niri.
|
||||
|
||||
## Portable profiles
|
||||
|
||||
|
|
@ -26,6 +26,9 @@ account has a usable password before switching from Gaming Mode.
|
|||
|
||||
Jovian's “Switch to Desktop” now selects `niri`. In Noctalia's launcher, choose
|
||||
“Return to Gaming Mode” to invoke Jovian's `steamosctl switch-to-game-mode`.
|
||||
Controller-only use (one app per screen, the L5-held Steam Input layer that
|
||||
sends Niri chords, and the Kando wheel) is described in
|
||||
`home/profiles/steamdeck/CONTROLLER.md`; none of it reaches the other hosts.
|
||||
Steam Deck now uses Nyx's vendored Jovian, with nixpkgs and Home Manager following
|
||||
Nyx's pins independently of Framework's unstable packages. The current gaming
|
||||
stack selects `7.2.4-valve1`; its Valve kernel patches remain enabled. Update
|
||||
|
|
@ -215,7 +218,7 @@ Do not point generated output at an immutable Home Manager symlink.
|
|||
| yazi | `noctalia` flavor for both modes, including syntax colors. |
|
||||
| Claude Code | Generated in personal and work profile theme directories. Select Noctalia with `/theme`; restart once if newly added. |
|
||||
| Codex | `tui.theme = "noctalia"`; start a new session to pick up syntax colors. |
|
||||
| Discord | Midnight community CSS generated for Vencord/Vesktop. Requires a compatible client/mod and enabling the theme; stock Discord cannot load it. |
|
||||
| Discord | Midnight community CSS written to Vesktop's theme directory. The official client cannot load it and is no longer installed. `vesktop.nix` seeds `noctalia.theme.css` into Vencord's `enabledThemes` at activation, and Vencord watches the themes directory, so switches re-inject without a restart. |
|
||||
| fcitx5 | Classic UI theme selected on Framework; native D-Bus reload hook. |
|
||||
| fzf | Community color options read through `FZF_DEFAULT_OPTS_FILE` on each invocation. Open a fresh login shell after activation. |
|
||||
| jjui | `ui.theme = "noctalia"` reading a generated `themes/noctalia.toml`. Stylix's target is off: its static `ui.colors` is jjui's highest-priority layer and masked every other one. A running jjui recolors when the terminal reports the new polarity; otherwise reopen it. |
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@
|
|||
./steamdeck
|
||||
];
|
||||
|
||||
"nixos@iso" = [
|
||||
./iso/base.nix
|
||||
./iso
|
||||
];
|
||||
|
||||
"bogdan@phantom" = [
|
||||
./phantom
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
# programs
|
||||
../../programs
|
||||
../../programs/vesktop.nix
|
||||
../../programs/keepassxc.nix
|
||||
../../programs/wayland
|
||||
../../programs/wayland/noctalia-templates.nix
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
home = {
|
||||
username = "nixos";
|
||||
homeDirectory = "/home/nixos";
|
||||
stateVersion = "25.11";
|
||||
};
|
||||
|
||||
# Let Home Manager install and manage itself
|
||||
programs.home-manager.enable = true;
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
imports = [
|
||||
# Editors - vital!
|
||||
../../editors/nvim
|
||||
|
||||
# Terminal configuration
|
||||
../../terminal
|
||||
../../terminal/emulators/ghostty.nix
|
||||
|
||||
# Wayland/Hyprland essentials
|
||||
../../programs/wayland
|
||||
../../programs/gtk.nix
|
||||
../../programs/gpg.nix
|
||||
|
||||
# Desktop services
|
||||
../../services/gpg.nix
|
||||
../../services/media/playerctl.nix
|
||||
|
||||
# Tailscale auto-auth (prompts for YubiKey PIN after login)
|
||||
./tailscale.nix
|
||||
];
|
||||
|
||||
# Generic Hyprland settings for any display
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
# Auto-detect monitors
|
||||
monitor = [
|
||||
", preferred, auto, 1"
|
||||
];
|
||||
|
||||
# Generic touchpad settings
|
||||
input = {
|
||||
touchpad = {
|
||||
natural_scroll = true;
|
||||
tap-to-click = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
# Tailscale auto-authentication for live ISO (user session)
|
||||
# Runs after graphical login so pinentry-qt can prompt for YubiKey PIN
|
||||
{ config, pkgs, inputs, ... }:
|
||||
|
||||
let
|
||||
secretsFile = "${inputs.secrets}/secrets/iso/tailscale.yaml";
|
||||
in {
|
||||
# Systemd user service to authenticate Tailscale after graphical login
|
||||
systemd.user.services.tailscale-autoconnect = {
|
||||
Unit = {
|
||||
Description = "Authenticate to Tailscale with YubiKey-decrypted auth key";
|
||||
# Run after graphical session is ready (pinentry-qt needs display)
|
||||
After = [ "graphical-session.target" ];
|
||||
Wants = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
|
||||
# Environment for pinentry and age-plugin-yubikey
|
||||
Environment = [
|
||||
"PINENTRY_PROGRAM=${pkgs.pinentry-qt}/bin/pinentry-qt"
|
||||
];
|
||||
|
||||
ExecStart = pkgs.writeShellScript "tailscale-autoconnect" ''
|
||||
set -euo pipefail
|
||||
|
||||
echo "Tailscale auto-connect: checking status..."
|
||||
|
||||
# Check if already authenticated
|
||||
status="$(${pkgs.tailscale}/bin/tailscale status -json 2>/dev/null | ${pkgs.jq}/bin/jq -r '.BackendState' 2>/dev/null || echo 'Unknown')"
|
||||
|
||||
if [ "$status" = "Running" ]; then
|
||||
echo "Tailscale already running, skipping authentication"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Tailscale auto-connect: decrypting auth key..."
|
||||
echo "You may be prompted for your YubiKey PIN..."
|
||||
|
||||
# Decrypt the auth key using sops
|
||||
# This will trigger pinentry-qt for the YubiKey PIV PIN
|
||||
AUTH_KEY="$(${pkgs.sops}/bin/sops -d --extract '["tailscale_auth_key"]' ${secretsFile} 2>/dev/null)" || {
|
||||
echo "ERROR: Failed to decrypt Tailscale auth key"
|
||||
echo "Make sure your YubiKey is connected and try: tailscale up"
|
||||
# Send desktop notification
|
||||
${pkgs.libnotify}/bin/notify-send -u critical "Tailscale" "Failed to decrypt auth key. Run 'tailscale up' manually."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ -z "$AUTH_KEY" ]; then
|
||||
echo "ERROR: Auth key is empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Tailscale auto-connect: authenticating..."
|
||||
${pkgs.tailscale}/bin/tailscale up --authkey="$AUTH_KEY" --operator=nixos
|
||||
|
||||
echo "Tailscale authenticated successfully!"
|
||||
${pkgs.libnotify}/bin/notify-send -u normal "Tailscale" "Connected to Tailscale network"
|
||||
'';
|
||||
};
|
||||
|
||||
Install = {
|
||||
WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
# Ensure required packages are available
|
||||
home.packages = with pkgs; [
|
||||
age-plugin-yubikey
|
||||
sops
|
||||
libnotify
|
||||
];
|
||||
}
|
||||
|
|
@ -1,79 +1,160 @@
|
|||
# Steam Input desktop controls
|
||||
|
||||
The Steam Deck profile declares Kando 2.3.1's menu/configuration, a Niri-only
|
||||
startup service, Thunar, and Steam's extest
|
||||
Wayland input compatibility. Steam starts silently with Niri to supply Desktop
|
||||
Layout mappings. Gaming Mode keeps its per-game layouts.
|
||||
Niri has no gamepad support of its own. On the Deck every controller press
|
||||
reaches the desktop through Steam Input's Desktop Layout, which emits X11 fake
|
||||
input; the `extest` shim (`programs.steam.extest`) turns that into a uinput
|
||||
device (`extest fake device`) that libinput and Niri read like a keyboard and
|
||||
mouse. That path was verified working on 2026-09-14: the 64-bit client loads
|
||||
the shim, the kernel creates the device, and native Wayland windows receive it.
|
||||
The `wrong ELF class: ELFCLASS32` journal lines come from Steam's 32-bit
|
||||
helpers and are harmless.
|
||||
|
||||
## One-time Steam Input layout setup
|
||||
What was broken was the mapping. Steam loaded its stock `desktop_neptune.vdf`,
|
||||
which can only produce arrows, Enter, Escape, Space, Tab and mouse clicks.
|
||||
No Niri chord was reachable, and windows opened at half width on a 1280×800
|
||||
panel, so the focused column sat off-screen where the pointer could not reach.
|
||||
|
||||
In Desktop Mode, open Steam Settings → Controller → Desktop Layout → Edit.
|
||||
Start from a desktop layout with right trackpad mouse and R2 left click. Save
|
||||
the result as a personal layout called **Niri + Kando Desktop**.
|
||||
## Layout: one app per screen
|
||||
|
||||
1. Create an action-set layer named **App wheel**.
|
||||
2. In the base layout, assign L2 soft pull to **Hold Action Set Layer → App wheel**.
|
||||
Remove its old right-click binding and any full-pull command. Use L1 for
|
||||
right click instead. Avoid toggle or repeated activation on L2.
|
||||
3. Add commands to that same L2 soft pull for **Left Ctrl**, **Left Alt**, and
|
||||
**F12**. They must be held for the duration of the pull, with turbo disabled.
|
||||
Set F12's Fire Start Delay to 0.1 seconds so the modifiers arrive first.
|
||||
Do not override L2 inside the App wheel layer: it must retain the base
|
||||
layer's hold/release behaviour.
|
||||
4. In App wheel, set right trackpad to Mouse, with trackball disabled. Set
|
||||
right stick to Joystick Mouse. Start with moderate sensitivity and adjust
|
||||
on the Deck. Keep R2 as left click. Set B to Escape.
|
||||
5. Test **Ctrl+Alt+F12** on a keyboard first. Then hold L2, point towards an
|
||||
entry with the pad or stick, and release L2 to select. B cancels. Opening
|
||||
with a quick tap also allows ordinary point-and-click selection with R2.
|
||||
The Deck profile appends a catch-all Niri window rule with
|
||||
`open-maximized true`, so every tiled window opens as a maximized column and
|
||||
fills the panel. `open-maximized` touches tiled columns only; a
|
||||
`default-column-width` rule would also stretch floating Steam dialogs, which
|
||||
is why it is not used. Navigating the scrolling layout collapses to
|
||||
"previous / next column" and "previous / next workspace". Noctalia's Settings
|
||||
window is tiled rather than floated on the Deck: the shared 1080×920 float is
|
||||
taller than the panel. Everything else in `home/programs/wayland/niri.kdl` is
|
||||
untouched; the Framework and Phantom configurations do not see these rules.
|
||||
|
||||
The opening shortcut is reserved in the Deck's Niri configuration. Kando's
|
||||
Turbo Mode uses the held Ctrl/Alt state to select on release. The exact Steam
|
||||
Input timing and modifier handoff require testing on the Deck; if release
|
||||
selection does not work, use the tap-and-click path while diagnosing it.
|
||||
Do not add a release-time mouse click: it could click the newly launched app.
|
||||
## Steam Input layout: stock desktop + a held Niri layer
|
||||
|
||||
Steam owns its live controller files. This repository does not overwrite
|
||||
account-specific Steam userdata or claim a layout has already been installed.
|
||||
After hardware verification, export the personal layout from Steam and retain
|
||||
the actual exported VDF alongside this document for reproducible re-import.
|
||||
`controller_neptune_niri_desktop.vdf` is Valve's `desktop_neptune.vdf` with one
|
||||
addition: an action layer named **Niri** that is active while the **L5** back
|
||||
grip is held. The stock layout bound L5 to a bare Super key, which does nothing
|
||||
on its own; everything else in the base layer is unchanged (right pad mouse,
|
||||
R2/L2 clicks, A Enter, B Escape, X Steam keyboard, Y Space, D-pad arrows,
|
||||
Steam-button chords).
|
||||
|
||||
## Menu
|
||||
While L5 is held:
|
||||
|
||||
| Input | Sends | Niri action |
|
||||
| --- | --- | --- |
|
||||
| D-pad left / right | Super+H / Super+L | Focus column left / right (wraps) |
|
||||
| D-pad up / down | Super+K / Super+J | Focus workspace up / down |
|
||||
| A | Super+Space | Noctalia launcher |
|
||||
| B | Super+Q | Close window |
|
||||
| X | Super+O | Overview |
|
||||
| Y | Super+Alt+K | Toggle on-screen keyboard (wvkbd) |
|
||||
| L1 / R1 | Super+Shift+H / L | Move column left / right |
|
||||
| Start (≡) | Super+A | Deck app wheel (Kando) |
|
||||
| Select (⧉) | Super+Ctrl+Space | Noctalia control center |
|
||||
| L4 | Super+Ctrl+G | Return to Gaming Mode |
|
||||
| R4 | Super+Escape | Previous workspace |
|
||||
| R5 | Super+Shift+F | Fullscreen window |
|
||||
|
||||
Super+A, Super+Ctrl+G and Super+Alt+K are Deck-only binds declared in
|
||||
`controller-desktop.nix`; every other chord is a shared binding from
|
||||
`niri.kdl`. The layer never rebinds L5 itself, so release always drops it.
|
||||
A raw capture of the extest device on 2026-09-14 showed every chord arriving
|
||||
with its modifiers held (for example LEFTSHIFT+LEFTMETA+H), so Steam's
|
||||
repeated-`binding` chord encoding is confirmed on hardware.
|
||||
|
||||
## On-screen keyboard
|
||||
|
||||
Steam's keyboard (X in the base layer) is an override-redirect X11 surface.
|
||||
Niri never lists it, cannot apply rules to it, and it ends up covering the
|
||||
field being typed into. It still works for Steam's own windows, but the
|
||||
desktop keyboard is **wvkbd**: a Wayland layer-shell keyboard that requests
|
||||
an exclusive zone, so the focused window shrinks above it rather than being
|
||||
overlapped, and types through the virtual-keyboard protocol into any client.
|
||||
|
||||
`deck-wvkbd.service` starts it hidden with Niri (280px landscape height,
|
||||
static Rosé Pine colours). Toggle it with L5+Y, with the **Keyboard** button
|
||||
next to **Apps** in the bar, or by tapping the panel and using it as a touch
|
||||
keyboard. The toggle is `systemctl --user kill --signal=SIGRTMIN
|
||||
deck-wvkbd.service`; SIGUSR1 hides and SIGUSR2 shows if a script needs a
|
||||
definite state.
|
||||
|
||||
### Installing the layout
|
||||
|
||||
Home Manager activation installs the file into Steam's template directory,
|
||||
`~/.local/share/Steam/controller_base/templates/`, which is where Steam looks
|
||||
for additional layouts and lists them under **Templates** in its picker (the
|
||||
same mechanism RetroDECK uses). The copy is repeated on every activation, so a
|
||||
Steam client update that prunes the directory is undone by the next rebuild.
|
||||
If Steam has not been bootstrapped yet the step is skipped with a message.
|
||||
|
||||
Selecting it is a one-time Steam GUI step, because Steam owns the per-account
|
||||
selection file and rewrites it:
|
||||
|
||||
1. Desktop Mode → Steam → Settings → Controller → **Desktop Layout** → Edit.
|
||||
2. Open the layout picker, choose the **Templates** tab, select **Niri Desktop**.
|
||||
3. Confirm. `~/.local/share/Steam/logs/controller_ui.txt` should then report
|
||||
the desktop config path as the template instead of
|
||||
`controller_base/desktop_neptune.vdf`.
|
||||
|
||||
Steam's action-layer ordinal is positional: Default = 1, the stock Gamepad set
|
||||
= 2, the Niri layer = 3. `hold_layer 3 0 0` in the base L5 binding refers to
|
||||
that ordinal, the same numbering the stock `CHANGE_PRESET 2 1 1` uses. Adding
|
||||
another action set before the layer would renumber it.
|
||||
|
||||
### Regenerating the file
|
||||
|
||||
The VDF is a static asset; do not hand-edit Steam's live copy. To rebuild it
|
||||
from a newer stock layout, copy `controller_base/desktop_neptune.vdf` off the
|
||||
Deck and re-run the generator used on 2026-09-14 (it replaces the L5 binding,
|
||||
adds the layer, three groups with `"layer" "1"`, and the layer preset). Keep
|
||||
the derived file in this directory so the repository is the source of truth.
|
||||
|
||||
## Menu (Kando)
|
||||
|
||||
Clockwise from the top: Firefox, Steam, Kodi, Files (Thunar), Terminal (Ghostty),
|
||||
Desktop controls. Controls contains Overview, Settings, Audio, Network,
|
||||
Gaming Mode, and All apps (Noctalia). Menu positions and commands are declared
|
||||
in `controller-desktop.nix`; editing Kando's GUI does not persist those changes.
|
||||
|
||||
The menu opens centred, with anchored submenus and enlarged directional targets.
|
||||
Kando's direct gamepad reader is disabled because Steam Input supplies pointer
|
||||
events. Noctalia and existing keyboard bindings remain available.
|
||||
Kando opens on **Super+A** (L5 + Start) or by tapping **Apps** at the left of
|
||||
the 42px Noctalia bar. Point with the right pad or stick, select with R2,
|
||||
cancel with B. Turbo Mode is off: the earlier design opened the wheel on a
|
||||
timed Ctrl+Alt+F12 hold and selected on modifier release, which depended on
|
||||
Steam Input's modifier hand-off timing and was never made to work.
|
||||
|
||||
## Verify on the Deck after activation
|
||||
|
||||
- Native Firefox and Thunar accept trackpad motion, R2 clicks and keyboard
|
||||
input while Steam is running. Verify again after suspend/resume.
|
||||
- Hold L2: one menu opens; no repeated opening, accidental right click, or
|
||||
stuck Ctrl/Alt after selection or cancellation. Check both pad and stick.
|
||||
- Each of the six entries launches; submenu actions affect the previous
|
||||
desktop window after Kando closes.
|
||||
- Gaming Mode returns through `steamosctl switch-to-game-mode`; the Kando
|
||||
service stops when Niri exits and is absent from the Gaming Mode session.
|
||||
- Test handheld and docked menu placement. Upstream describes Niri support as
|
||||
having some flaws, so evaluation alone does not verify window placement.
|
||||
- `ls ~/.local/share/Steam/controller_base/templates/ | grep niri` shows the file;
|
||||
Steam's Templates tab lists **Niri Desktop**; select it once.
|
||||
- Without L5: stock behaviour, nothing new. Hold L5 and press D-pad
|
||||
left/right: Firefox, Steam and Heroic swap places one at a time, each filling
|
||||
the panel. Release L5: D-pad is arrows again, no stuck Super.
|
||||
- L5+A opens the launcher. L5+Y raises wvkbd along the bottom edge and the
|
||||
launcher shrinks above it; type with pad clicks or touch, L5+Y hides it.
|
||||
L5+B closes the focused window. L5+X shows the overview; pad-click a window.
|
||||
- L5+Start opens the wheel; each of the six entries launches; R2 selects.
|
||||
- L5+L4 returns to Gaming Mode; the Kando service is absent there.
|
||||
- Retest after suspend/resume and after a Steam client update.
|
||||
|
||||
Steam supplies the only on-screen keyboard. The Deck disables
|
||||
The Deck disables
|
||||
Noctalia's lockscreen, startup lock, idle lock, and lock-before-suspend. Jovian's
|
||||
automatic login enters Desktop Mode without a password prompt. SSH remains
|
||||
key-authenticated; this does not assign a blank Unix account password.
|
||||
|
||||
## Access without a physical keyboard
|
||||
## Steam menus and dialogs
|
||||
|
||||
Kando opens **Deck Apps** when Desktop Mode starts. To reopen it, click or tap
|
||||
**Apps** at the left of the Noctalia bar. The Deck bar is 42 pixels tall to make
|
||||
this control easier to hit. This button does not require a custom Steam
|
||||
Input layout or opening Noctalia settings.
|
||||
Steam's menu bar, context menus and the friends-list menus are X11
|
||||
override-redirect popups. xwayland-satellite 0.8.2 reports the pointer leaving
|
||||
them right after they map, so they flicker shut and anything behind them
|
||||
(Games → Add a Non-Steam Game) cannot be reached. The Deck pins 0.8.1 through
|
||||
Niri's `xwayland-satellite { path }` option in `xwayland-satellite-pin.nix`;
|
||||
Niri spawns that binary on demand, so a fresh session picks it up. If a menu
|
||||
still misbehaves, `steam steam://AddNonSteamGame` opens the dialog directly.
|
||||
|
||||
Known follow-ups, not addressed here: Framework's five named workspaces still
|
||||
appear on the Deck strip; Steam starts with `-pipewire` and its recording
|
||||
feature raises a "Share Screen" portal dialog on sign-in; wvkbd colours do not
|
||||
follow Mod+F5 (no runtime reload).
|
||||
|
||||
References: [Kando navigation](https://kando.menu/usage/),
|
||||
[Niri integration](https://kando.menu/installation-on-linux/),
|
||||
[configuration](https://kando.menu/config-files/).
|
||||
[Kando configuration](https://kando.menu/config-files/),
|
||||
[RetroDECK layout install via templates](https://github.com/RetroDECK/RetroDECK/wiki/Controllers:-Steam-Deck),
|
||||
[VDF action-layer syntax example](https://github.com/jsantorek/steamdeck-gw2-layout/blob/main/controller_neptune.vdf).
|
||||
|
|
|
|||
|
|
@ -13,13 +13,29 @@ let
|
|||
delayed = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Steam Input Desktop Layout: Valve's desktop_neptune.vdf plus an action
|
||||
# layer held on the L5 grip that sends Super chords for the shared Niri
|
||||
# binds (see CONTROLLER.md for the table). Steam lists any *.vdf found in
|
||||
# controller_base/templates under "Templates" in its layout picker; the file
|
||||
# name only has to be unique there.
|
||||
layoutFile = ./controller_neptune_niri_desktop.vdf;
|
||||
layoutTarget = "${config.home.homeDirectory}/.local/share/Steam/controller_base/templates/controller_neptune_niri_desktop.vdf";
|
||||
|
||||
# Steam's own on-screen keyboard is an override-redirect X11 surface: Niri
|
||||
# never lists it, cannot place it, and it lands on top of whatever field is
|
||||
# being filled. wvkbd is a layer-shell keyboard that requests an exclusive
|
||||
# zone, so the focused window shrinks above it instead of being covered,
|
||||
# and it types through the virtual-keyboard protocol into any Wayland
|
||||
# client. SIGRTMIN toggles it; it starts hidden.
|
||||
wvkbdToggle = [ "${pkgs.systemd}/bin/systemctl" "--user" "kill" "--signal=SIGRTMIN" "deck-wvkbd.service" ];
|
||||
in {
|
||||
home.packages = [ pkgs.kando pkgs.thunar ];
|
||||
home.packages = [ pkgs.kando pkgs.thunar pkgs.wvkbd ];
|
||||
|
||||
# Accessible before any Steam Input custom layout has been configured.
|
||||
programs.noctalia.settings = {
|
||||
bar.default.thickness = lib.mkForce 42;
|
||||
bar.default.start = lib.mkBefore [ "deck-apps" ];
|
||||
bar.default.start = lib.mkBefore [ "deck-apps" "deck-keyboard" ];
|
||||
widget.deck-apps = {
|
||||
type = "custom_button";
|
||||
glyph = "apps";
|
||||
|
|
@ -27,6 +43,13 @@ in {
|
|||
tooltip = "Open Deck Apps";
|
||||
actions.left = "exec ${pkgs.kando}/bin/kando --menu 'Deck Apps'";
|
||||
};
|
||||
widget.deck-keyboard = {
|
||||
type = "custom_button";
|
||||
glyph = "keyboard";
|
||||
label = "Keyboard";
|
||||
tooltip = "Toggle on-screen keyboard";
|
||||
actions.left = "exec ${lib.escapeShellArgs wvkbdToggle}";
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."kando/config.json".text = builtins.toJSON {
|
||||
|
|
@ -36,7 +59,10 @@ in {
|
|||
showIntroductionDialog = false;
|
||||
enableAchievements = false;
|
||||
enableAchievementNotifications = false;
|
||||
enableTurboMode = true;
|
||||
# Turbo Mode selects on modifier release. The wheel now opens from a tap
|
||||
# (Super+A or the bar button), so there is no held modifier to release;
|
||||
# selection is point-and-click with R2, B cancels.
|
||||
enableTurboMode = false;
|
||||
enableGamepad = false; # Steam Input supplies pointer events; avoid double input.
|
||||
keepInputFocus = false;
|
||||
enableSelectionWedges = true;
|
||||
|
|
@ -84,16 +110,42 @@ in {
|
|||
}];
|
||||
};
|
||||
|
||||
# Deck-only binds, inserted into the shared binds block for this host only.
|
||||
# Plain Super+letter chords: Steam Input sends them as two held sub-commands
|
||||
# (LEFT_WINDOWS + key), which is the most timing-tolerant shape it offers.
|
||||
# Every other chord the layout sends is an existing shared bind in niri.kdl.
|
||||
# Niri owns startup and teardown. Do not register Kando's XDG autostart:
|
||||
# graphical-session.target is also used by Jovian's Gaming Mode.
|
||||
desktop.niri.extraBinds = ''
|
||||
Ctrl+Alt+F12 repeat=false hotkey-overlay-title="Deck app wheel" {
|
||||
Mod+A repeat=false hotkey-overlay-title="Deck app wheel" {
|
||||
spawn "${pkgs.kando}/bin/kando" "--menu" "Deck Apps";
|
||||
}
|
||||
Mod+Ctrl+G hotkey-overlay-title="Return to Gaming Mode" {
|
||||
spawn "/run/current-system/sw/bin/steamosctl" "switch-to-game-mode";
|
||||
}
|
||||
Mod+Alt+K repeat=false hotkey-overlay-title="Toggle on-screen keyboard" {
|
||||
spawn ${lib.concatMapStringsSep " " (a: "\"${a}\"") wvkbdToggle};
|
||||
}
|
||||
'';
|
||||
desktop.niri.extraConfig = ''
|
||||
spawn-at-startup "steam" "-silent"
|
||||
spawn-at-startup "${pkgs.systemd}/bin/systemctl" "--user" "start" "deck-kando.service"
|
||||
spawn-at-startup "${pkgs.systemd}/bin/systemctl" "--user" "start" "deck-kando.service" "deck-wvkbd.service"
|
||||
|
||||
// 1280x800 handheld: every tiled window opens as a maximized column, so
|
||||
// the focused app always fills the panel and the pointer can reach all
|
||||
// of it. Navigating the scrolling layout becomes previous/next column.
|
||||
// open-maximized only affects tiled columns; floating Steam dialogs keep
|
||||
// their own size (default-column-width would stretch those too).
|
||||
window-rule {
|
||||
open-maximized true
|
||||
}
|
||||
|
||||
// The shared config floats Settings at 1080x920, taller than this panel.
|
||||
// Later rules win per property: tile it and let it maximize instead.
|
||||
window-rule {
|
||||
match app-id=r#"^dev\.noctalia\.Noctalia$"#
|
||||
open-floating false
|
||||
}
|
||||
|
||||
window-rule {
|
||||
match title="^Kando Menu$"
|
||||
|
|
@ -119,4 +171,42 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
systemd.user.services.deck-wvkbd = {
|
||||
Unit = {
|
||||
Description = "Steam Deck on-screen keyboard (wvkbd)";
|
||||
PartOf = [ "niri.service" "graphical-session.target" ];
|
||||
After = [ "graphical-session.target" ];
|
||||
ConditionEnvironment = "WAYLAND_DISPLAY";
|
||||
};
|
||||
Service = {
|
||||
# Landscape height applies on the 1280x800 panel; colors are static
|
||||
# Rosé Pine (wvkbd has no runtime theme reload).
|
||||
ExecStart = lib.escapeShellArgs [
|
||||
"${pkgs.wvkbd}/bin/wvkbd-mobintl"
|
||||
"--hidden"
|
||||
"-L" "280"
|
||||
"-R" "6"
|
||||
"--fn" "Hack Nerd Font Mono 15"
|
||||
"--bg" "191724" "--fg" "26233a" "--fg-sp" "403d52"
|
||||
"--press" "524f67" "--press-sp" "6e6a86"
|
||||
"--swipe" "524f67" "--swipe-sp" "6e6a86"
|
||||
"--text" "e0def4" "--text-sp" "908caa"
|
||||
];
|
||||
Restart = "on-failure";
|
||||
RestartSec = 2;
|
||||
};
|
||||
};
|
||||
|
||||
# Steam owns controller_base; a client update may recreate it. Re-install
|
||||
# the template on every activation so the repository stays the source of
|
||||
# truth. Selecting it is a one-time GUI step: the per-account configset
|
||||
# file is Steam's, and it rewrites that file.
|
||||
home.activation.deckControllerLayout = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
templates="$(dirname ${lib.escapeShellArg layoutTarget})"
|
||||
if [ -d "$templates" ]; then
|
||||
run install -m 0644 ${layoutFile} ${lib.escapeShellArg layoutTarget}
|
||||
else
|
||||
echo "deck controller layout: $templates not found (Steam not bootstrapped yet); skipped"
|
||||
fi
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
1679
home/profiles/steamdeck/controller_neptune_niri_desktop.vdf
Normal file
1679
home/profiles/steamdeck/controller_neptune_niri_desktop.vdf
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -3,10 +3,13 @@
|
|||
{
|
||||
imports = [
|
||||
./controller-desktop.nix
|
||||
./xwayland-satellite-pin.nix
|
||||
./syncthing.nix
|
||||
../../programs/vesktop.nix
|
||||
../../programs/keepassxc.nix
|
||||
../../programs/games/minecraft
|
||||
../../programs/games/openrct2
|
||||
../../programs/games/heroic
|
||||
# Configured Claude Code (settings/agents/MCP/plugins under ~/.claude). Required
|
||||
# by the openrct2 coding-agent fork, whose in-game terminal launches `claude`.
|
||||
../../terminal/programs/claude
|
||||
|
|
|
|||
37
home/profiles/steamdeck/xwayland-satellite-pin.nix
Normal file
37
home/profiles/steamdeck/xwayland-satellite-pin.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{ pkgs, ... }:
|
||||
|
||||
let
|
||||
# xwayland-satellite 0.8.2 (commit 3273a0f) reports the pointer as leaving
|
||||
# Steam's override-redirect menu popups about 35 ms after they map, so every
|
||||
# menu-bar and context menu closes the instant it opens, and anything only
|
||||
# reachable through those menus (Games → Add a Non-Steam Game) is unreachable.
|
||||
# 0.8.1 renders them correctly.
|
||||
# https://github.com/Supreeeme/xwayland-satellite/issues/503
|
||||
# https://github.com/niri-wm/niri/issues/4517
|
||||
# https://github.com/ValveSoftware/steam-for-linux/issues/13566
|
||||
# Deck-only: Niri is pointed at this binary through its own config option, so
|
||||
# the shared niri.kdl and the other hosts keep whatever nixpkgs ships. Drop
|
||||
# this file once a release past 0.8.2 fixes the popup pointer-leave.
|
||||
xwaylandSatellite = pkgs.xwayland-satellite.overrideAttrs (old: rec {
|
||||
version = "0.8.1";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "Supreeeme";
|
||||
repo = "xwayland-satellite";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-BUE41HjLIGPjq3U8VXPjf8asH8GaMI7FYdgrIHKFMXA=";
|
||||
};
|
||||
# buildRustPackage derives cargoDeps from cargoHash at call time, so the
|
||||
# vendor derivation has to be replaced, not the hash.
|
||||
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
|
||||
inherit src;
|
||||
hash = "sha256-16L6gsvze+m7XCJlOA1lsPNELE3D364ef2FTdkh0rVY=";
|
||||
};
|
||||
});
|
||||
in {
|
||||
desktop.niri.extraConfig = ''
|
||||
// Pinned 0.8.1; see xwayland-satellite-pin.nix.
|
||||
xwayland-satellite {
|
||||
path "${xwaylandSatellite}/bin/xwayland-satellite"
|
||||
}
|
||||
'';
|
||||
}
|
||||
77
home/programs/games/heroic/default.nix
Normal file
77
home/programs/games/heroic/default.nix
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Heroic Games Launcher — the Epic/GOG/Amazon client, added here to run Rocket League.
|
||||
#
|
||||
# Why a launcher and not a package
|
||||
# Rocket League cannot be a nixpkgs derivation. Psyonix dropped the native Linux
|
||||
# build in March 2020 and delisted the game from Steam that September; it now
|
||||
# exists only as a free-to-play, Windows-only title on the Epic Games Store.
|
||||
# So the store path holds the *client*, and the game itself is runtime state that
|
||||
# Heroic downloads into ~/Games/Heroic (or wherever you point it — an SD card
|
||||
# works). Same split as ../openrct2, which packages the engine but leaves the
|
||||
# RCT2 game data to a first-launch prompt.
|
||||
#
|
||||
# Epic has never shipped an Epic Games Launcher for Linux, so an Epic-owned copy
|
||||
# needs a third-party client regardless. Epic's own support page saying no such
|
||||
# software is needed on SteamOS is scoped to the *Steam*-owned copy ("You can do
|
||||
# the launch via Steam if you own it there") — Steam supplies Proton itself.
|
||||
# Heroic (which drives Legendary underneath) is what speaks Epic's API here.
|
||||
#
|
||||
# `heroic`, NOT `heroic-unwrapped`
|
||||
# These are two different packages and picking the wrong one breaks at runtime,
|
||||
# not at build time. `heroic` is `steam.buildRuntimeEnv` wrapping
|
||||
# `heroic-unwrapped` — i.e. the Steam FHS sandbox. That matters because Heroic's
|
||||
# whole job is executing foreign, dynamically-linked binaries: the Windows game,
|
||||
# and any Wine/Proton build it downloads itself. Outside an FHS environment those
|
||||
# fail with the usual misleading "No such file or directory" (the missing path is
|
||||
# /lib64/ld-linux-x86-64.so.2, not the binary) — exactly the trap in CLAUDE.md.
|
||||
# heroic-unwrapped launches fine and then cannot run a single game.
|
||||
#
|
||||
# Anti-cheat — supported, not merely tolerated
|
||||
# Psyonix shipped Easy Anti-Cheat on 2026-04-28 and explicitly enabled EAC's
|
||||
# Linux/Proton path; Epic's support pages name Steam Deck and Linux as supported
|
||||
# platforms. EAC is opt-out (there is a "Play without Easy Anti-Cheat" launch
|
||||
# option) but online matchmaking requires it on, which also means BakkesMod and
|
||||
# other mods are offline/LAN-only now. ProtonDB rates the game Platinum (0.88
|
||||
# over ~864 reports, trending Platinum), and GamingOnLinux's 2026-04-30 test of
|
||||
# the post-EAC build was made through Heroic specifically.
|
||||
#
|
||||
# heroic-unwrapped also puts `umu-launcher` on Heroic's PATH on x86_64-linux
|
||||
# (see its makeWrapper call). umu is the Proton runtime shim that reproduces
|
||||
# Steam's environment for non-Steam launches, including the EAC bits. Nothing to
|
||||
# configure; it just has to be reachable, and nixpkgs handles that.
|
||||
#
|
||||
# The EOS Overlay is NOT supported on Linux/Steam Deck per Epic's own EOS docs,
|
||||
# and will not load however it is toggled. It is cosmetic — unrelated to EAC —
|
||||
# so this is not a problem to chase.
|
||||
#
|
||||
# Proton
|
||||
# Heroic's own Rocket League wiki page says the game "should work just fine out
|
||||
# of the box with GE-Proton". Get that from Heroic's built-in Wine Manager tab
|
||||
# rather than adding pkgs.protonup-qt: Heroic downloads GE-Proton into
|
||||
# ~/.config/heroic/tools, and because this package IS the Steam FHS runtime (see
|
||||
# above), that downloaded build executes correctly — the one case where pulling a
|
||||
# prebuilt binary from the internet is fine on NixOS, since it never leaves the
|
||||
# sandbox. protonup-qt only becomes worth adding if you also want GE-Proton
|
||||
# registered for Steam itself.
|
||||
#
|
||||
# Adding it to Gaming Mode
|
||||
# heroic-unwrapped ships com.heroicgameslauncher.hgl.desktop (Exec=heroic) and the
|
||||
# FHS wrapper symlinks share/applications + share/icons through, so Desktop Mode
|
||||
# sees it without a hand-written xdg.desktopEntry.
|
||||
#
|
||||
# When adding it via Steam's "Add a Non-Steam Game", point the shortcut at
|
||||
# /home/deck/.nix-profile/bin/heroic
|
||||
# and NOT at the /nix/store/<hash>-heroic/bin/heroic that the file browser will
|
||||
# land on. Steam bakes the absolute path into shortcuts.vdf, and a store path is
|
||||
# invalidated by the next rebuild that bumps Heroic — the shortcut then dies on
|
||||
# the following GC, from Gaming Mode, with no useful error. ~/.nix-profile/bin is
|
||||
# the stable indirection: this host runs Home Manager with useUserPackages = false
|
||||
# (see hosts/default.nix — the steamdeck system does not import
|
||||
# system/programs/home-manager.nix), so HM installs into the user profile there.
|
||||
#
|
||||
# Heroic can also write the Steam shortcut itself ("Add to Steam" per game), which
|
||||
# avoids hand-picking a path.
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
home.packages = [ pkgs.heroic ];
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ in
|
|||
{
|
||||
home.packages = with pkgs; [
|
||||
pkgs-unstable.telegram-desktop
|
||||
discord-ptb
|
||||
signal-desktop
|
||||
element-desktop
|
||||
];
|
||||
|
|
|
|||
55
home/programs/vesktop.nix
Normal file
55
home/programs/vesktop.nix
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
vesktopDir = "${config.xdg.configHome}/vesktop";
|
||||
|
||||
# Vencord resolves its data dir from VENCORD_USER_DATA_DIR, which Vesktop
|
||||
# sets to its own Electron userData dir. So themes live in
|
||||
# <vesktopDir>/themes and Vencord's own state in <vesktopDir>/settings.
|
||||
themeFile = "noctalia.theme.css";
|
||||
|
||||
# Vencord persists all plugin and theme state to settings/settings.json, so
|
||||
# declaring `programs.vesktop.vencord.settings` would link it in read-only
|
||||
# and break the in-app settings panel. Seed the one key we care about into
|
||||
# the mutable file instead, appending so Noctalia lands last in the cascade
|
||||
# and never reordering what is already enabled.
|
||||
enableTheme = pkgs.writeShellApplication {
|
||||
name = "vesktop-enable-noctalia-theme";
|
||||
runtimeInputs = with pkgs; [ jq coreutils ];
|
||||
text = ''
|
||||
dir="$1"
|
||||
settings="$dir/settings/settings.json"
|
||||
mkdir -p "$dir/themes" "$dir/settings"
|
||||
if [ -f "$settings" ]; then
|
||||
tmp="$(mktemp "$settings.XXXXXX")"
|
||||
jq --arg t "${themeFile}" \
|
||||
'if (.enabledThemes // []) | index($t) then .
|
||||
else .enabledThemes = ((.enabledThemes // []) + [$t]) end' \
|
||||
"$settings" > "$tmp"
|
||||
mv "$tmp" "$settings"
|
||||
else
|
||||
jq -n --arg t "${themeFile}" '{ enabledThemes: [ $t ] }' > "$settings"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
in
|
||||
# Vesktop replaces the official client: it is the only Discord client that can
|
||||
# load the Noctalia theme, and Vencord live-reloads it on every Mod+F5.
|
||||
{
|
||||
programs.vesktop = {
|
||||
enable = true;
|
||||
|
||||
# Take Vencord from nixpkgs instead of letting Vesktop fetch and
|
||||
# self-update its own copy at runtime.
|
||||
vencord.useSystem = true;
|
||||
|
||||
# Intentionally no `settings` / `vencord.settings` — see enableTheme above.
|
||||
};
|
||||
|
||||
# Vencord reads settings.json once at startup (it watches the themes dir and
|
||||
# quickCss.css, but not its own settings), so a newly seeded theme applies
|
||||
# from the next Vesktop launch onwards.
|
||||
home.activation.vesktopNoctaliaTheme = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
run ${lib.getExe enableTheme} ${lib.escapeShellArg vesktopDir}
|
||||
'';
|
||||
}
|
||||
|
|
@ -399,6 +399,7 @@ window-rule {
|
|||
// comms → named workspaces, hidden from screencasts
|
||||
window-rule {
|
||||
match app-id=r#"(?i)^discord"#
|
||||
match app-id=r#"(?i)^vesktop"#
|
||||
match app-id=r#"^org\.telegram\.desktop$"#
|
||||
match app-id=r#"(?i)^signal"#
|
||||
open-on-workspace "chat"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@
|
|||
yazi.enable = false;
|
||||
opencode.enable = false;
|
||||
obsidian.enable = false;
|
||||
# Wrote ~/.config/Vencord/themes/stylix.theme.css for Vencord-injected-into-
|
||||
# Discord, a client this config no longer installs. Vesktop reads
|
||||
# ~/.config/vesktop/themes instead, so the file was simply dead.
|
||||
vencord.enable = false;
|
||||
# Pre-asserts `vencord.settings.enabledThemes = [ "stylix.css" ]`, which
|
||||
# would both mask Noctalia's live theme and freeze Vencord's writable
|
||||
# settings.json as a store symlink. vesktop.nix seeds the theme instead.
|
||||
vesktop.enable = false;
|
||||
zathura.enable = false;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,25 @@ let
|
|||
fzfTemplate = pkgs.runCommand "noctalia-fzf-template" { } ''
|
||||
sed -n '/^--color=/p' ${source}/fzf/fzf.sh | sed 's/"$//' > "$out"
|
||||
'';
|
||||
# Midnight spends its three dimmest text slots on Material's outline roles,
|
||||
# which are specified for dividers (~3:1, ~1.5:1) rather than for text. Under
|
||||
# this dark palette that puts message body at 5.5:1, channel names at 3.0:1
|
||||
# and timestamps at 1.7:1 against the chat background - the last two below
|
||||
# WCAG AA. Shift each slot one rung up the on-surface scale, which does carry
|
||||
# a text contrast guarantee, and let outline_variant fall out of text duty.
|
||||
# This rewrites which role is read, not the color, so live retheming and the
|
||||
# other palettes (light mode included) keep working.
|
||||
discordTemplate = pkgs.runCommand "noctalia-discord-midnight" { } ''
|
||||
cp ${source}/discord/discord-midnight.css "$out"
|
||||
chmod u+w "$out"
|
||||
substituteInPlace "$out" \
|
||||
--replace-fail '--text-3: {{colors.on_surface_variant.default.hex}};' \
|
||||
'--text-3: {{colors.on_surface.default.hex}};' \
|
||||
--replace-fail '--text-4: {{colors.outline.default.hex}};' \
|
||||
'--text-4: {{colors.on_surface_variant.default.hex}};' \
|
||||
--replace-fail '--text-5: {{colors.outline_variant.default.hex}};' \
|
||||
'--text-5: {{colors.outline.default.hex}};'
|
||||
'';
|
||||
# Upstream builds beside apply.sh, which is read-only in the Nix store.
|
||||
libreofficeSource = pkgs.runCommand "noctalia-libreoffice" { } ''
|
||||
cp -r ${source}/libreoffice "$out"
|
||||
|
|
@ -41,10 +60,11 @@ in
|
|||
"${userHome}/.claude-work/themes/noctalia.json"
|
||||
];
|
||||
codex = template "codex/codex.tmTheme" "${userHome}/.codex/themes/noctalia.tmTheme";
|
||||
discord = template "discord/discord-midnight.css" [
|
||||
"${cfg}/Vencord/themes/noctalia.theme.css"
|
||||
"${cfg}/vesktop/themes/noctalia.theme.css"
|
||||
];
|
||||
# Vesktop points Vencord at its own userData dir, so themes live here.
|
||||
discord = {
|
||||
input_path = "${discordTemplate}";
|
||||
output_path = "${cfg}/vesktop/themes/noctalia.theme.css";
|
||||
};
|
||||
fcitx5 = (template "fcitx5/theme.conf" "${config.xdg.dataHome}/fcitx5/themes/noctalia/theme.conf") // {
|
||||
post_hook = "${pkgs.systemd}/bin/busctl --user call org.fcitx.Fcitx5 /controller org.fcitx.Fcitx.Controller1 ReloadAddonConfig s classicui";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
homeImports = import "${self}/home/profiles";
|
||||
mod = "${self}/system";
|
||||
|
||||
inherit (import mod) laptop steamdeck iso phantom;
|
||||
inherit (import mod) laptop steamdeck phantom;
|
||||
|
||||
specialArgs = { inherit inputs self;};
|
||||
# Shared modules may select unstable applications. Scope those selections
|
||||
|
|
@ -89,37 +89,6 @@
|
|||
];
|
||||
};
|
||||
|
||||
# Live ISO - portable NixOS with Framework13-like environment
|
||||
live-iso = nixosSystem {
|
||||
inherit specialArgs;
|
||||
modules = iso ++ [
|
||||
./iso
|
||||
|
||||
"${mod}/programs/hyprland"
|
||||
|
||||
# Home Manager (stable, follows nixpkgs)
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
|
||||
# Secrets management (for Yubikey-based sops decryption)
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
|
||||
# Private NixOS config (from the private flake)
|
||||
inputs.secrets.nixosModules.default
|
||||
|
||||
{
|
||||
home-manager = {
|
||||
users.nixos.imports = homeImports."nixos@iso";
|
||||
extraSpecialArgs = specialArgs;
|
||||
backupFileExtension = ".hm-backup";
|
||||
sharedModules = [
|
||||
inputs.sops-nix.homeManagerModules.sops
|
||||
inputs.nix4nvchad.homeManagerModules.default
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Phantom USB - ephemeral NixOS that leaves no trace
|
||||
# LUKS-encrypted, impermanent root, WireGuard home overlay
|
||||
phantom = nixosSystem {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
# Inheritance structure:
|
||||
# base → desktop → laptop
|
||||
# → iso
|
||||
# → phantom (ephemeral USB with impermanence)
|
||||
# base → steamdeck (uses Jovian-NixOS for desktop)
|
||||
|
||||
|
|
@ -56,16 +55,6 @@ let
|
|||
./services/fprintd.nix
|
||||
];
|
||||
|
||||
# Live ISO - portable system with Hyprland environment
|
||||
# Extends desktop with network auth and YubiKey support
|
||||
# Note: boot.nix excluded - ISO uses its own boot configuration
|
||||
iso = desktop ++ [
|
||||
./network/tailscale.nix # Base Tailscale service
|
||||
./network/tailscale-iso.nix # Auto-auth with sops-encrypted key
|
||||
|
||||
./services/yubikey.nix # For sops decryption with age-plugin-yubikey
|
||||
];
|
||||
|
||||
# Phantom USB - ephemeral system with impermanence
|
||||
# Extends desktop with LUKS boot, impermanence, and paranoid security
|
||||
# Root is tmpfs - only /persist survives reboot
|
||||
|
|
@ -98,5 +87,5 @@ let
|
|||
];
|
||||
|
||||
in {
|
||||
inherit laptop steamdeck iso phantom;
|
||||
inherit laptop steamdeck phantom;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue