Commit Graph

54 Commits

Author SHA1 Message Date
9a276a633a lact: reduce undervolt 2026-05-07 17:33:15 -04:00
7cf27f0cda forza-trigger: remove RPM-stuck workaround + bump hysteresis to 120
Two separate causes of stationary trigger pulsing, both fixed:

1. Hysteresis too short. 30 packets (0.5s) was shorter than FH5's
   stationary oscillation period (~1s per state in start-grid/pause
   screen contexts). Bumped to 120 (2s at 60Hz).

2. RPM-stuck workaround removed entirely. Cosmii's RPM_ACCUMULATOR
   (legacy_Program.cs:89-109) forced is_race_on false after 3.3s of
   constant RPM + zero power. While stationary in-race (idle RPM
   constant, power near zero), this would trip, causing a false
   menu transition. Engine idle flutter on power could reset and
   re-trigger it, producing a slow oscillation with clicks on each
   edge. FH5 has been observed to correctly clear is_race_on between
   races (confirmed via live strace), so the workaround is unnecessary.

   Removed: RPM_ACCUMULATOR_TRIGGER_RACE_OFF constant, is_race_on's
   debounce logic, DaemonState.last_rpm and .rpm_accumulator fields.
   is_race_on is now a one-liner: return bool(is_race_on field).

Tests: 57/57 pass. TestIsRaceOn simplified from 4 to 3 tests.
DaemonState reset test no longer checks removed fields.
2026-05-07 17:29:40 -04:00
5798caef37 forza-trigger: hysteresis on is_race_on (real fix for between-race clicks)
Strace post-deploy showed the daemon flipping every other packet between
in-race state (mode 0x21 FEEDBACK + LED green) and out-of-race state
(mode 0x05 OFF + LED black). 8 writes, 8 transitions in 5s — every
HID OUT report a state change.

Root cause: FH5 emits packets where the is_race_on field alternates
True/False at packet rate during menu/loading/transition states.
Cosmii's RPM_ACCUMULATOR debounce only handles the 'flag stuck True
when we're really in a menu' case (quirk #1); it does nothing for
'flag flipping at packet rate' (quirk #2).

Fix: split the read from the hysteresis. is_race_on now returns the
raw flag with quirk #1 applied. commit_in_race applies a packet-count
hysteresis (IN_RACE_HYSTERESIS_PACKETS = 30 ≈ 0.5s at 60Hz) — only
after N consecutive packets of the new value does the committed
in-race state flip. Alternation just keeps the pending counter
oscillating near 0; it never reaches threshold and the run-loop sees
a stable state.

Architecture: hysteresis in a separate function (not in is_race_on)
because the run-loop must update state.last_in_race AFTER side
effects, not before — otherwise the elif transition-detection
breaks. is_race_on stays pure read; commit_in_race is the gatekeeper;
run-loop sets state.last_in_race once at end of packet handling.

Tests grew 54→58. New TestCommitInRace covers:
  - stable input (no pending growth)
  - worst-case alternation (never commits)
  - N consecutive packets commit
  - matching raw resets pending counter

TestIsRaceOn renamed to reflect new (narrower) responsibility.
2026-05-07 15:11:35 -04:00
b9cdc6a9b7 forza-trigger: stop per-packet writes between races (fix periodic clicks)
User reports periodic clicks and LED color changes on the controller
"between races". Theory: out-of-race, the run loop was calling
reset_triggers(controller) AND apply_lightbar(controller, ...) on
every Forza packet (60Hz). Even though both call sites land in
library code that nominally dedupes "same state" writes, in practice
any state change anywhere — including the lightbar dropping a green
channel value as RPM coasts down to idle — triggers a full HID OUT
report rewrite. The OUT report carries the trigger configuration too;
the controller firmware reacts on receipt and produces an audible
click each time.

Fix: out-of-race path becomes edge-triggered. On the in-race → menu
transition we run state.reset() + reset_all() once (turning both
triggers off and the lightbar to (0,0,0)). Subsequent menu packets
make no controller calls at all until in_race flips back to True.
First in-race packet then re-engages handlers and the RPM-driven
lightbar.

Side effect: the menu-mode car-class lightbar coloring is gone — the
bar stays black between races. If we want it back later, it should
be one-shot on the menu transition (NOT updated per-packet). For now
keep it simple: in-race only.

Build clean; tests unchanged (54/54 still pass — they exercise
handlers directly, not the run loop).
2026-05-07 15:00:59 -04:00
c4c9dd7e50 forza-trigger: patch dualsense-controller for vibration freq propagation
All checks were successful
Build and Deploy / mreow (push) Successful in 1m6s
Build and Deploy / yarn (push) Successful in 1m7s
Build and Deploy / muffin (push) Successful in 1m6s
Live strace on yarn during gameplay revealed the daemon was correctly
calling effect.vibration(freq=35) on slip events, but the OUT report
on the wire showed mode=0x26 (VIBRATION) with byte 31 (param9 =
frequency) = 0. The controller firmware treats freq=0 as "no
oscillation" — trigger sits silent in vibration mode. That's why the
user reported "doesn't react to slipping or anything" even after the
pedal-off early-return fix.

Root cause is in dualsense-controller 0.3.1 itself:
  - WriteStates.__init__ registers individual write-states for trigger
    effect params 1-7 only. params 8/9/10 are not registered.
  - update_out_report copies from per-state values to the OutReport,
    again only params 1-7.
  - The OutReport dataclass DEFINES params 1-10 (with defaults of 0)
    and Usb01OutReport.to_bytes writes all 10 to the wire.
  - effect.vibration() puts frequency in param9 — silently dropped.
  - Same hits effect.machine() (params 8,9,10) and effect.galloping()
    (param10). effect.feedback/weapon/bow only use params 1-7 so
    they happen to work.

Fix is a small upstream-style patch added under patches/dualsense-
controller/ and wired into the dualsense-controller derivation in
hosts/yarn/forza-trigger/python-packages.nix via the patches attr:
in update_out_report, after the param7 assignments, read param8/9/10
directly from the parent TriggerEffect state value (which already
carries them correctly from the call site through _set_value).

Verified post-patch by source-reading the installed library:
  out_report.left_trigger_effect_param9 = self.left_trigger_effect.value.param9
  out_report.right_trigger_effect_param9 = self.right_trigger_effect.value.param9
  (and 4 more for left/right param8/10)

Build-sandbox tests (54/54) pass via the forza-trigger-tests build
gate; full yarn NixOS closure builds clean.

Filing upstream against yesbotics/dualsense-controller-python is the
follow-up; until then this is a local patch.
2026-05-07 14:38:12 -04:00
11d283585c forza-trigger: drop pedal-off early-returns (root cause of "no reaction")
Live diagnostic on yarn revealed the daemon was receiving 324-byte FH5
packets correctly (5.7MB on the systemd socket; strace showed steady
recvfrom + write to /dev/hidraw7) but writing trigger mode 0x05
(no-resistance) on nearly every tick. Cause: `accel` and `brake` are
0 most of the time during normal play (off-throttle on straight
sections, off-brake when not braking). Both handlers had:

  if accel/255 <= THROTTLE_INPUT_THRESHOLD: effect.off(); return
  if brake/255 <= BRAKE_INPUT_THRESHOLD:    effect.off(); return

Every off-pedal packet set the trigger to OFF. Brief pedal-on moments
set vibration. The result: rapidly oscillating off↔vibration state,
imperceptible at 60 Hz packet rate.

These early-returns were holdovers from the previous Race-Element 1:1
port (variant A), which IS designed to be silent unless slipping.
Variant D's whole point is "always feels something" — Cosmii has no
pedal-off gate, and its baseline branch produces feedback even at
brake=0/accel=0 with strength clamped to MIN.

Fix: remove both early-returns. Foot-off-pedal flows through the
baseline branch and produces feedback(strength=MIN_*_RESISTANCE). The
user feels light constant resistance instead of silence. Trigger only
returns to physical-rest when out-of-race (run-loop's reset_triggers).

Also drop the now-dead BRAKE_INPUT_THRESHOLD / THROTTLE_INPUT_THRESHOLD
constants. Two tests renamed and updated to assert MIN-strength
baseline feedback instead of effect.off() on zero pedal.

54/54 tests pass. Build clean.
2026-05-07 14:18:47 -04:00
e159f4d90f dualsense: possibly fix weird vibrations
All checks were successful
Build and Deploy / mreow (push) Successful in 1m1s
Build and Deploy / yarn (push) Successful in 1m6s
Build and Deploy / muffin (push) Successful in 1m25s
2026-05-07 01:27:53 -04:00
03c3d01c66 forza-trigger: things 2026-05-07 01:27:49 -04:00
8ba6decc1f firefly-iii-data-importer: init
All checks were successful
Build and Deploy / mreow (push) Successful in 1m38s
Build and Deploy / yarn (push) Successful in 55s
Build and Deploy / muffin (push) Successful in 1m40s
2026-05-05 02:05:16 -04:00
82213c2917 firefly-iii: init
All checks were successful
Build and Deploy / mreow (push) Successful in 1m14s
Build and Deploy / yarn (push) Successful in 55s
Build and Deploy / muffin (push) Successful in 1m58s
2026-05-05 01:40:29 -04:00
6501fe2ddb forza-trigger: rewrite 2026-05-04 14:47:00 -04:00
71b797730a fh5: use proton experimental
All checks were successful
Build and Deploy / mreow (push) Successful in 3m33s
Build and Deploy / yarn (push) Successful in 1m25s
Build and Deploy / muffin (push) Successful in 1m6s
2026-05-03 22:47:49 -04:00
1fc2f995c7 yarn: forza dualsense adaptive trigger bridge 2026-05-03 22:47:41 -04:00
4d0ba317e1 Revert "lact: disable undervolt"
This reverts commit 6e69b40b4e.
2026-05-03 22:45:12 -04:00
a65a7e7bda steam-config-nix: move to my fork and drop gameMods 2026-05-03 22:45:11 -04:00
1686a2ecc5 remove stupid comment from optiscalar config 2026-05-03 22:44:15 -04:00
e4b3c19d00 yarn: drop GE-Proton compat-tool pin from steam-config-nix
nixpkgs' proton-ge-bin (the package wired into programs.steam.extra-
CompatPackages via modules/desktop-steam.nix) registers in Steam's
compat-tool list under its versioned id, currently GE-Proton10-34.
steam-config-nix's README example uses the unversioned string
"GE-Proton", which on a fresh boot wrote that literal value into
localconfig.vdf — Steam resolved it to no installed tool and silently
fell back to bundled Proton 10. FH5 then launched on stock Proton,
which doesn't pick up PROTON_FSR4_UPGRADE the way GE does.

Drop both `compatTool` (per-app) and `defaultCompatTool` (global).
The wrapper-based launchOptions.env path is unaffected — env vars
still pass through to whatever Proton Steam ends up using. Tool
selection goes back to manual Steam UI > Properties > Compatibility.

A versioned pin (`compatTool = "GE-Proton10-34";`) would work but
couples the host config to whatever the proton-ge-bin nixpkgs entry
ships this week; not worth the maintenance.
2026-05-03 22:44:14 -04:00
2404792b61 game-mods: drop in-house launchOptions writer, hardcode FH5 ini
Replaces three handfuls of custom code with upstream / static data:

- Per-app Steam launch options now declared via different-name/steam-
  config-nix's `programs.steam.config.apps.<n>` instead of a custom
  ~70-line `apply_launch_options` Python function. The dropped writer
  was racy: it edited localconfig.vdf without checking for a running
  Steam, so any timer firing while Steam was open would lose its
  changes on the next Steam shutdown. steam-config-nix's `closeSteam`
  flag closes that race.

  Also moves the GE-Proton compat-tool pin to declarative config —
  one fewer manual click in Steam UI to remember.

- `mods.<>.launchOptions` option, the `launchOptionsData` aggregation,
  and `LAUNCH_OPTIONS_DATA` are removed from desktop-game-mods.nix.
  The module now does file-drops only; Steam config lives in its own
  `programs.steam.config` namespace, where it belongs.

  fh5-vkd3d-no-hvv (which existed only to set VKD3D_CONFIG) collapses
  into the FH5 launchOptions block in hosts/yarn/default.nix.

- `unitConfig.X-ConfigHash` on game-mods.service is replaced with
  `restartTriggers`. NixOS already emits `X-Restart-Triggers=<hash>`
  on the unit; the workaround was redundant. The Type=oneshot,
  RemainAfterExit=no semantics make `systemctl restart` re-run
  ExecStart cleanly on hash change.

- The awk pipeline that patched OptiScaler's stock OptiScaler.ini at
  build time is replaced with a hand-written hosts/yarn/optiscaler-
  fh5-rdna3.ini containing only the keys we override (5 of them).
  OptiScaler's Config::readString defaults missing keys to "auto"
  (Config.cpp:1568), so a minimal file is sufficient. Side benefits:
  one upstream-source dependency removed, a key-rename in upstream
  becomes a behavior change rather than a silent awk-no-match.

  Override values + sources:
    Fsr4Update=true              FH5 wiki, FSR4 Linux Setup
    DlssReactiveMaskBias=0.65    FH5 wiki, "Known Issues"
    FsrNonLinearColorSpace=true  FSR4 wiki, "Image Quality"
    EnableFsr2Inputs=false       FH5 wiki, "Known Issues"
    Dxgi=false                   FH5 wiki

- forza-trigger's three custom Python derivations (pydualsense,
  hidapi-usb, fdp) factored out of default.nix into a sibling
  python-packages.nix. Same logic, single-purpose file. Bumping a
  version is now a one-place hash roll.

- pkgs.dualsensectl removed from the daemon's environment.system-
  Packages. Single-shot writes from the CLI get clobbered by the BG
  sendReport thread within ~4ms anyway, so the tool is only useful
  with the daemon stopped — not worth the unconditional install.
  Bring it in ad-hoc with `nix-shell -p dualsensectl`.
2026-05-03 22:44:14 -04:00
510d2702b7 yarn: FH5 OptiScaler FSR 4 + VKD3D upload-hvv workaround
Drops OptiScaler v0.9.1 + a FH5-tuned OptiScaler.ini into the FH5
install dir to unlock FSR 4 INT8 on this RDNA 3 (Navi 32) box.
OptiScaler intercepts FH5's DLSS/XeSS calls and reroutes them through
the bundled FFX SDK. Per the OptiScaler FH5 wiki page: rename
OptiScaler.dll to dxgi.dll, set Dxgi=false, DlssReactiveMaskBias=0.65,
and Fsr4Update=true for the INT8 RDNA 3 path.

Sets Steam launch options PROTON_FSR4_UPGRADE=1 and
DXIL_SPIRV_CONFIG=wmma_rdna3_workaround on fh5-optiscaler (the FSR 4
wiki documents both as required for RDNA 3 on Linux).

fh5-vkd3d-no-hvv is its own mod (no files, just one launchOptions
entry for VKD3D_CONFIG=no_upload_hvv) so the upload-hvv workaround
can be removed when a future Proton release fixes the underlying
issue without disturbing the OptiScaler config.

Extends the intro skip stub to cover the hires variant of the
T10/Microsoft Studios splash; the engine picks SD or hires based on
the installed asset profile, so stub both per PCGamingWiki.
2026-05-03 22:43:54 -04:00
21a32b76b7 game-mod: extend module 2026-05-03 22:43:32 -04:00
50568aa01b steamos: disable steam deck cmdlineConfig for non-steamdeck hosts 2026-05-03 22:43:32 -04:00
483961ac1b lact: disable undervolt 2026-05-03 22:43:31 -04:00
a1d2090ff2 game-mods: init
Add override for fh5 startup video
2026-05-03 22:43:30 -04:00
d475e368c2 lact: -130 -> -120 2026-05-03 22:42:43 -04:00
004d5962bb lact: -150 -> -130 2026-05-03 22:42:43 -04:00
975c4f7af1 yarn: declarative lact config 2026-05-03 00:35:48 -04:00
394b890008 yarn: add impermanence for bluetooth devices (doesn't forget them now) 2026-05-03 00:35:48 -04:00
44a5d01960 yarn: mount /var/lib/agenix in initrd
All checks were successful
Build and Deploy / mreow (push) Successful in 2m16s
Build and Deploy / yarn (push) Successful in 1m3s
Build and Deploy / muffin (push) Successful in 1m6s
agenix activation runs from initrd-nixos-activation-start, which fires
right after /sysroot/persistent is mounted but before impermanence's
stage-2 bind mounts. The TPM identity at /var/lib/agenix/tpm-identity
was therefore unreadable at activation time, and every secret silently
failed to decrypt: 'no readable identities found'. Visible downstream
fallout was pull-update-apply hitting HTTP 401 against the binary cache
because nix-cache-netrc was never written to /run/agenix.

Mark /var/lib/agenix as neededForBoot via a bare fileSystems entry,
mirroring the existing /home/${username} bind. Drop the now-redundant
environment.persistence directory entry to avoid two competing units.
2026-04-27 17:42:40 -04:00
2ab1c855ec Revert "muffin: test, move to 7.0"
All checks were successful
Build and Deploy / mreow (push) Successful in 1m45s
Build and Deploy / yarn (push) Successful in 47s
Build and Deploy / muffin (push) Successful in 1m31s
This reverts commit f67ec5bde6.
2026-04-25 10:50:00 -04:00
f67ec5bde6 muffin: test, move to 7.0
Some checks failed
Build and Deploy / mreow (push) Successful in 1h43m17s
Build and Deploy / yarn (push) Successful in 22m1s
Build and Deploy / muffin (push) Failing after 33s
2026-04-25 02:12:21 -04:00
86cf624027 Revert "muffin: test, move to 6.18"
All checks were successful
Build and Deploy / mreow (push) Successful in 50s
Build and Deploy / yarn (push) Successful in 44s
Build and Deploy / muffin (push) Successful in 1m2s
This reverts commit 1df3a303f5.
2026-04-24 14:21:40 -04:00
1df3a303f5 muffin: test, move to 6.18
All checks were successful
Build and Deploy / mreow (push) Successful in 1m15s
Build and Deploy / yarn (push) Successful in 43s
Build and Deploy / muffin (push) Successful in 1m29s
2026-04-24 14:08:26 -04:00
07a5276e40 patiodeck: fix disko partition order (fixed-size before 100%) 2026-04-24 01:47:25 -04:00
f3d21f16fb desktop-jovian: unify steam/jovian config across yarn + patiodeck
- modules/desktop-jovian.nix: shared Jovian deck-mode config (unfree
  predicate, jovian.steam, sddm, gamescope override, imports
  desktop-steam-update.nix)
- home/progs/steam-shortcuts.nix: declarative non-Steam shortcuts
  (Prism Launcher); add new entries here for all Jovian hosts
- hosts/yarn/default.nix: reduced to host-specific config only
- hosts/patiodeck/default.nix: same
2026-04-23 22:42:25 -04:00
5b2a1a652a patiodeck: add prism launcher to steam shortcuts 2026-04-23 22:34:58 -04:00
665793668d patiodeck: add steam deck LCD host 2026-04-23 22:34:47 -04:00
5ccd84c77e yarn: fix steamos-update exit code — 7 means no update, not 0
Some checks failed
Build and Deploy / mreow (push) Successful in 1m48s
Build and Deploy / yarn (push) Successful in 4m39s
Build and Deploy / muffin (push) Failing after 31s
Steam interprets exit 0 from 'steamos-update check' as 'update applied
successfully' and shows a persistent 'update available' notification.
The SteamOS convention is exit 7 = no update available.
2026-04-23 20:47:33 -04:00
b41a547589 yarn: persist root fish history
Some checks failed
Build and Deploy / mreow (push) Successful in 46s
Build and Deploy / yarn (push) Successful in 51s
Build and Deploy / muffin (push) Failing after 28s
2026-04-23 20:17:02 -04:00
e019f2d4fb secrets overhaul: use tpm for laptop (need to migrate desktop later) 2026-04-23 14:22:37 -04:00
22282691e7 grafana: add minecraft server stats 2026-04-23 01:17:10 -04:00
0901f5edf0 deploy: potentially fix self-deploy issue? 2026-04-22 23:02:38 -04:00
d00ff42e8e site-config: dedupe cross-host values, fix stale dark-reader urls, drop desktop 1g hugepages
new site-config.nix holds values previously duplicated across hosts:
  domain, old_domain, contact_email, timezone, binary_cache (url + pubkey),
  dns_servers, lan (cidr + gateway), hosts.{muffin,yarn} (ip/alias/ssh_host_key),
  ssh_keys.{laptop,desktop,ci_deploy}.

threaded through specialArgs on all three hosts + home-manager extraSpecialArgs +
homeConfigurations.primary + serverLib. service-configs.nix now takes
{ site_config } as a function arg and drops its https namespace; per-service
domains (gitea/matrix/ntfy/mollysocket/livekit/firefox-sync/grafana) are
derived from site_config.domain. ~15 service files and 6 vm tests migrated.

breakage fixes rolled in:
 - home/progs/zen/dark-reader.nix: 5 stale *.gardling.com entries in
   disabledFor rewritten to *.sigkill.computer (caddy 301s the old names so
   these never fired and the new sigkill urls were getting dark-reader applied)
 - modules/desktop-common.nix: drop unused hugepagesz=1G/hugepages=3
   kernelParams (no consumer on mreow or yarn; xmrig on muffin still reserves
   its own via services/monero/xmrig.nix)

verification: muffin toplevel is bit-identical to pre-refactor baseline.
mreow/yarn toplevels differ only in boot.json kernelParams + darkreader
storage.js (nix-diff verified). deployGuardTest and fail2banVaultwardenTest
(latter exercises site_config.domain via bitwarden.nix) pass.
2026-04-22 20:48:29 -04:00
8cdb9c4381 yarn: improve pull-update-apply script
Some checks failed
Build and Deploy / mreow (push) Successful in 2m3s
Build and Deploy / yarn (push) Successful in 1m3s
Build and Deploy / muffin (push) Failing after 28s
2026-04-22 20:11:22 -04:00
3902ad5de3 yarn: fix jovian-stubs
Some checks failed
Build and Deploy / mreow (push) Successful in 1m9s
Build and Deploy / yarn (push) Successful in 4m36s
Build and Deploy / muffin (push) Failing after 33s
2026-04-22 19:54:00 -04:00
0538907674 yarn: simplify stubs
Some checks failed
Build and Deploy / mreow (push) Successful in 41s
Build and Deploy / yarn (push) Failing after 1m8s
Build and Deploy / muffin (push) Failing after 1m39s
2026-04-22 19:44:53 -04:00
90ce41cd9e gitea: move gitea-runner user declaration to actions-runner.nix
Some checks failed
Build and Deploy / mreow (push) Successful in 55s
Build and Deploy / yarn (push) Failing after 58s
Build and Deploy / muffin (push) Has started running
2026-04-22 19:24:18 -04:00
1be21b6c52 split off terminal utilities 2026-04-22 18:45:00 -04:00
c3cc94a305 merge common-*.nix files
Some checks failed
Build and Deploy / mreow (push) Successful in 1h37m19s
Build and Deploy / muffin (push) Has been cancelled
Build and Deploy / yarn (push) Has been cancelled
2026-04-22 18:02:05 -04:00
0c8b8232c2 yarn: disable steamos-mandatory-update
All checks were successful
Build and Deploy / mreow (push) Successful in 52s
Build and Deploy / yarn (push) Successful in 4m33s
Build and Deploy / muffin (push) Successful in 1m10s
2026-04-22 11:46:37 -04:00
aef99e7365 deploy-guard: block activation while users are online
Some checks failed
Build and Deploy / mreow (push) Successful in 51s
Build and Deploy / yarn (push) Successful in 47s
Build and Deploy / muffin (push) Failing after 1m9s
- modules/server-deploy-guard.nix: extendable aggregator registered via
  services.deployGuard.checks.<name>.{description,command}. Installs
  deploy-guard-check with per-check timeout, pass/block reporting, JSON
  output, DEPLOY_GUARD_BYPASS / /run/deploy-guard-bypass (single-shot).
- services/jellyfin/jellyfin-deploy-guard.nix: curl+jq on /Sessions,
  blocks when any session carries NowPlayingItem; soft-fails when unreachable.
- services/minecraft-deploy-guard.nix: mcstatus SLP query on 25565, blocks
  when players.online > 0; soft-fails when unreachable.
- flake.nix: wrap deploy.nodes.muffin activation with activate.custom so
  deploy-guard-check runs before switch-to-configuration. Auto-rollback
  catches the failure. dryActivate/boot branches preserved.
- deploy.sh: SSH preflight for ./deploy.sh muffin with --force /
  DEPLOY_GUARD_FORCE=1 (touches remote bypass marker). Connectivity
  failure is soft; activation still enforces.
- tests/deploy-guard.nix: aggregator contract, bypass mechanics, timeout,
  JSON output.
2026-04-22 00:36:21 -04:00