Commits
The hardcoded `export SWAYSOCK=/run/user/$(id -u)/sway-ipc.sock` in
extraSessionCommands is ignored by sway >=1.11, which creates its own per-pid
socket (sway-ipc.$uid.$pid.sock). The dead path got baked into the firefox and
workspace-manager user services, so every swaymsg call failed and the extension
silently skipped tab restore on each workspace switch. Drop the export and let
the existing import-environment propagate the real socket sway advertises.
Also:
- workspace-menu: "Restore tabs" used pkgs.firefox (upstream stable) instead of
the wrapped Dev Edition daemon, launching a separate browser/profile
- workspace-tabs: never overwrite a saved workspace with an empty tab list, so
the transient blank window after a Firefox restart can't wipe real tabs
Assisted-by: Claude Code (claude-opus-4-8[1m])
It's too close to shift+insert (paste) and the default reset binding
scrambles running programs on accidental presses.
Assisted-by: Claude Code (claude-opus-4-8[1m])
mumble-reminder: add 26.05 Release Animal Yarara
- firefox.service: restartIfChanged/stopIfChanged = false so nixos-rebuild
switch no longer kills all open browser windows
- xdg-desktop-portal-wlr: retry until system-wide pipewire is reachable
(RestartSec=2, startLimitIntervalSec=0) instead of tripping the start-limit
at boot and staying dead, which silently broke screensharing
- workspace-tabs: register the anchor-window scratchpad rule statically in the
sway config, only persist/restore http(s) URLs, and skip anchor-titled
windows in the sway mapping, so the anchor no longer leaks into workspace
files and reappears as a phantom window after a restart
Assisted-by: Claude Code (claude-opus-4-8[1m])
The btmtk WMT-funcc fix (e3ac0d9f1a20) is now upstream; the override
fails to apply against 7.0.10. Use linuxPackages_latest directly.
Assisted-by: Claude Code (claude-opus-4-7)
Adjusts /var/download/games to ipfs:users mode 2775 so the upload
service (running as ipfs) can write into it while lass keeps write via
the users group. systemd-tmpfiles refuses to bootstrap this transition
when child and parent have different owners, so the first chown was
done out-of-band; this `z` rule keeps the perms from drifting.
Also drops the non-existent `download` group from the staging dir
tmpfiles entries (download group only exists on the yellow container,
not on the host).
Assisted-by: Claude Code (claude-opus-4-7)
Adds an ipfs-upload service (Python http.server on 127.0.0.1:8090) and
routes POSTs on ipfs.lassul.us to it (GET/HEAD still go to the kubo
gateway, via nginx upstream + map on $request_method).
Each chunk is written to a deterministic staging file under
/var/download/incoming/uploader/<sha256(rel)>.upload (same fs as the
destination, outside the pin-watcher's watch dirs). When the staging
size reaches the Content-Range total, it is atomically renamed to
/var/lib/ipfs/download/<rel> and pinned with ipfs add --nocopy --pin.
Protocol:
POST /<path> one-shot upload
POST /<path> with Content-Range bytes a-b/T resumable chunk
POST /<path> with Content-Length: 0 returns current offset
Assisted-by: Claude Code (claude-opus-4-7)
Pairing an Xbox Wireless Controller over Bluetooth (LE HoG) on stock
bluez 5.86 failed with "Connection terminated due to authentication
failure" on every connection attempt after pair. Enable xpadneo so the
hid_xpadneo driver handles the controller, and add two bluez tweaks
that resolve the LE auth loop:
* JustWorksRepairing = always: accept fresh pairings without requiring
a manual remove of the prior bond.
* Privacy = device: pin the host BT address so the controller's stored
bond key isn't invalidated when bluez rotates resolvable addresses.
Assisted-by: Claude Code (claude-opus-4-7[1m])
Upstream 634a4408c061 ("Bluetooth: btmtk: validate WMT event SKB
length before struct access"), backported into 7.0.7, rejects the
shorter WMT FUNC_CTRL events MT7925 firmware actually sends, leaving
hci0 stuck DOWN with "Failed to send wmt func ctrl (-22)".
Apply upstream e3ac0d9f1a20 ("Bluetooth: btmtk: accept too short
WMT FUNC_CTRL events") on top of linux_latest until the fix is
backported into linux-7.0.y stable. Remove this override once nixpkgs
ships a kernel that already contains the fix.
Assisted-by: Claude Code (claude-opus-4-7[1m])
Was killing remote tmux sessions the moment all ssh clients detached —
the actual cause of "tmux dies when everyone disconnects from neoprism".
Linger/KillUserProcesses were red herrings; tmux itself was tearing
the sessions down from inside.
Assisted-by: Claude Code (claude-opus-4-7)
Without lingering, systemd-logind tears down user@1337.service when the
last session disconnects, killing tmux along with the user slice. The
KillUserProcesses=no setting only prevents logind from explicitly killing
scope leftovers — it does not keep the user manager alive.
Assisted-by: Claude Code (claude-opus-4-7)
nixpkgs nginx is compiled with /tmp/nginx_* as temp paths. Combined
with systemd PrivateTmp, host-side cleanup of /tmp can leave nginx
with missing temp dirs after restart, breaking large POSTs (e.g.
matrix /keys/upload) and federation PUTs. Move to /var/cache/nginx,
which is auto-created via the systemd unit's CacheDirectory=nginx.
Assisted-by: Claude Code (claude-opus-4-7)
Confused agents that did plain `ipfs add` of files already on disk
caused ~9G of blockstore/filestore duplication and ~10G of
blockstore-only content. Shadow the CLI with a wrapper that rejects
`add` calls missing --nocopy; the kubo daemon and pin-watcher use
absolute store paths so they keep working.
Assisted-by: Claude Code (claude-opus-4-7)
Pin-watcher was creating MFS entries for aria2 control files that get
deleted once the download finishes, leaving orphans in the index. Also
ensure the cid-map file is world-readable after creation and after
reconcile rewrites it.
Assisted-by: Claude Code (claude-opus-4-7)
Upstream NixOS tinc module's preStart chowns hosts/ and invitations/
but not the network dir itself, so tincr can't persist the per-peer
address bootstrap cache it tries to write under /etc/tinc/retiolum/
addrcache/. The save fails with "Permission denied" (debug-only), the
cache stays empty, and retries to peers without a live meta-conn fall
back to DNS instead of the last-known address. Drop this tmpfiles rule
once nixpkgs#520533 lands.
Assisted-by: Claude Code (claude-opus-4-7)
New extension queries /api/tags at startup and registers every model
under a single "ollama" provider via the OpenAI-compat layer. Endpoint
points at neoprism.r:11434 — reachable from retiolum via the c-base-ai
autossh tunnel.
settings-env.patch also patches SettingsManager.reload() so the
PI_SETTINGS_FILE merge survives resourceLoader.reload() — without this
the bundled extensions list was being wiped after initial load.
Persistent autossh tunnel via tunnel.c-base.org exposing
ai.cbrp3.c-base.org:11434 on neoprism, reachable from retiolum at
neoprism.r:11434. ssh key generated by clan vars; pubkey must be
installed on tunnel.c-base.org under lassulus before the tunnel comes up.
PR Mic92/tincr#5 (merged 2026-05-11) removes the auto-purge in
on_del_edge that drove reachability oscillation in mixed
tincr/tinc-1.1pre18 meshes.
On top, pin Lassulus/tincr@a25eec05 which adds udp_addr clearing on
ENETUNREACH/EHOSTUNREACH/EAFNOSUPPORT/EADDRNOTAVAIL — without it,
IPv4-only hosts retry the same broken cached IPv6 udp_addr forever
when a peer's wire-advertised contact is v6 only. Submitted upstream
as Mic92/tincr#6.
Under load, the swaymsg get_tree subprocess could time out, the host
returned an empty windows list, and the extension treated that as "no
Firefox windows anywhere" — restoring a window for a workspace that
already had one. Distinguish query failure from a genuinely empty
result and skip restoration when state is unknown.
Adds `s say "..."` which speaks via flite TTS and posts a desktop
notification. The notify skill documents when to use it; the wrapper
registers the skill and allows `s` in Bash so calls don't prompt.
Upstream creates /var/lib/mautrix-discord at mode 0770 owned by
mautrix-discord:mautrix-discord, but only adds matrix-synapse to the
mautrix-discord-registration supplementary group. That left synapse
unable to traverse the directory to read discord-registration.yaml,
crashing matrix-synapse on startup.
dhtclient only announces to the DHT. lassie (and IPNI/cid.contact)
never learned about neoprism's pins, so all fetchIpfs calls fell back
to archive.org. autoclient keeps DHT client mode but also pushes
provider records to IPNI via HTTP, making content discoverable by
lassie without a fallbackUrl.
ns1.first-ns.de, robotns2.second-ns.de, robotns3.second-ns.com
tincr is wire-compatible with tinc 1.1pre18 (ExperimentalProtocol=yes,
Ed25519 only). Wrap the package with a /sbin -> bin symlink so
krebs.tinc's ${tincPackage}/sbin/tincd path resolves, and override
tincPackage. Existing peers all have Ed25519 keys; RSA keys in host
files are warned about and ignored.
The script was previously SSH_ASKPASS-only (raw PIN on stdout), which
broke rbw and gpg-agent: they speak the Assuan protocol over stdin/stdout
and could not parse the output ('failed to read password from pinentry').
Dispatch by argv:
- no args, or first arg starts with '-' (real pinentry flags like
--timeout 0 --ttyname ... --display ... --no-global-grab) -> Assuan mode
- bare prompt arg (SSH_ASKPASS convention used by ssh-tpm-agent)
-> raw PIN on stdout (existing behaviour)
Assuan mode handles SETTITLE/SETPROMPT/SETDESC/SETERROR/SETKEYINFO/OPTION/
RESET/NOP/BYE, percent-decodes incoming descriptions for the rofi -mesg,
and percent-encodes the D-line PIN.
Kernel-keyring caching is scoped to SSH_ASKPASS mode only; rbw and
gpg-agent do their own caching, and sharing one keyring slot across
unrelated secrets handed back the wrong PIN to whichever caller asked
second.
tools.kagi-search: default to 'rbw get kagi.com -f session_link'
Matches the actual vault layout (a kagi.com login with a session_link
field) instead of expecting a separate 'kagi-session-link' entry.
Bump the Workspace widget's characterCount to 20 so workspace names
fit (was capped at 2). Vertical bars are still capped to 2 chars by
noctalia regardless of this value.
When gpg-agent is reachable and the user has a secret encryption
subkey ([E] ssb), emit the age-plugin-gpg default identity
(AGE-PLUGIN-GPG-1H40XPM, the bech32 of an empty body for the gpg
plugin). gpg-agent caches the passphrase / smartcard PIN, so this is
more frictionless than the yubikey path; we therefore place it above
yubikey/solo2 but below se/tpm.
The hardcoded `export SWAYSOCK=/run/user/$(id -u)/sway-ipc.sock` in
extraSessionCommands is ignored by sway >=1.11, which creates its own per-pid
socket (sway-ipc.$uid.$pid.sock). The dead path got baked into the firefox and
workspace-manager user services, so every swaymsg call failed and the extension
silently skipped tab restore on each workspace switch. Drop the export and let
the existing import-environment propagate the real socket sway advertises.
Also:
- workspace-menu: "Restore tabs" used pkgs.firefox (upstream stable) instead of
the wrapped Dev Edition daemon, launching a separate browser/profile
- workspace-tabs: never overwrite a saved workspace with an empty tab list, so
the transient blank window after a Firefox restart can't wipe real tabs
Assisted-by: Claude Code (claude-opus-4-8[1m])
- firefox.service: restartIfChanged/stopIfChanged = false so nixos-rebuild
switch no longer kills all open browser windows
- xdg-desktop-portal-wlr: retry until system-wide pipewire is reachable
(RestartSec=2, startLimitIntervalSec=0) instead of tripping the start-limit
at boot and staying dead, which silently broke screensharing
- workspace-tabs: register the anchor-window scratchpad rule statically in the
sway config, only persist/restore http(s) URLs, and skip anchor-titled
windows in the sway mapping, so the anchor no longer leaks into workspace
files and reappears as a phantom window after a restart
Assisted-by: Claude Code (claude-opus-4-8[1m])
Adjusts /var/download/games to ipfs:users mode 2775 so the upload
service (running as ipfs) can write into it while lass keeps write via
the users group. systemd-tmpfiles refuses to bootstrap this transition
when child and parent have different owners, so the first chown was
done out-of-band; this `z` rule keeps the perms from drifting.
Also drops the non-existent `download` group from the staging dir
tmpfiles entries (download group only exists on the yellow container,
not on the host).
Assisted-by: Claude Code (claude-opus-4-7)
Adds an ipfs-upload service (Python http.server on 127.0.0.1:8090) and
routes POSTs on ipfs.lassul.us to it (GET/HEAD still go to the kubo
gateway, via nginx upstream + map on $request_method).
Each chunk is written to a deterministic staging file under
/var/download/incoming/uploader/<sha256(rel)>.upload (same fs as the
destination, outside the pin-watcher's watch dirs). When the staging
size reaches the Content-Range total, it is atomically renamed to
/var/lib/ipfs/download/<rel> and pinned with ipfs add --nocopy --pin.
Protocol:
POST /<path> one-shot upload
POST /<path> with Content-Range bytes a-b/T resumable chunk
POST /<path> with Content-Length: 0 returns current offset
Assisted-by: Claude Code (claude-opus-4-7)
Pairing an Xbox Wireless Controller over Bluetooth (LE HoG) on stock
bluez 5.86 failed with "Connection terminated due to authentication
failure" on every connection attempt after pair. Enable xpadneo so the
hid_xpadneo driver handles the controller, and add two bluez tweaks
that resolve the LE auth loop:
* JustWorksRepairing = always: accept fresh pairings without requiring
a manual remove of the prior bond.
* Privacy = device: pin the host BT address so the controller's stored
bond key isn't invalidated when bluez rotates resolvable addresses.
Assisted-by: Claude Code (claude-opus-4-7[1m])
Upstream 634a4408c061 ("Bluetooth: btmtk: validate WMT event SKB
length before struct access"), backported into 7.0.7, rejects the
shorter WMT FUNC_CTRL events MT7925 firmware actually sends, leaving
hci0 stuck DOWN with "Failed to send wmt func ctrl (-22)".
Apply upstream e3ac0d9f1a20 ("Bluetooth: btmtk: accept too short
WMT FUNC_CTRL events") on top of linux_latest until the fix is
backported into linux-7.0.y stable. Remove this override once nixpkgs
ships a kernel that already contains the fix.
Assisted-by: Claude Code (claude-opus-4-7[1m])
Without lingering, systemd-logind tears down user@1337.service when the
last session disconnects, killing tmux along with the user slice. The
KillUserProcesses=no setting only prevents logind from explicitly killing
scope leftovers — it does not keep the user manager alive.
Assisted-by: Claude Code (claude-opus-4-7)
nixpkgs nginx is compiled with /tmp/nginx_* as temp paths. Combined
with systemd PrivateTmp, host-side cleanup of /tmp can leave nginx
with missing temp dirs after restart, breaking large POSTs (e.g.
matrix /keys/upload) and federation PUTs. Move to /var/cache/nginx,
which is auto-created via the systemd unit's CacheDirectory=nginx.
Assisted-by: Claude Code (claude-opus-4-7)
Confused agents that did plain `ipfs add` of files already on disk
caused ~9G of blockstore/filestore duplication and ~10G of
blockstore-only content. Shadow the CLI with a wrapper that rejects
`add` calls missing --nocopy; the kubo daemon and pin-watcher use
absolute store paths so they keep working.
Assisted-by: Claude Code (claude-opus-4-7)
Upstream NixOS tinc module's preStart chowns hosts/ and invitations/
but not the network dir itself, so tincr can't persist the per-peer
address bootstrap cache it tries to write under /etc/tinc/retiolum/
addrcache/. The save fails with "Permission denied" (debug-only), the
cache stays empty, and retries to peers without a live meta-conn fall
back to DNS instead of the last-known address. Drop this tmpfiles rule
once nixpkgs#520533 lands.
Assisted-by: Claude Code (claude-opus-4-7)
New extension queries /api/tags at startup and registers every model
under a single "ollama" provider via the OpenAI-compat layer. Endpoint
points at neoprism.r:11434 — reachable from retiolum via the c-base-ai
autossh tunnel.
settings-env.patch also patches SettingsManager.reload() so the
PI_SETTINGS_FILE merge survives resourceLoader.reload() — without this
the bundled extensions list was being wiped after initial load.
PR Mic92/tincr#5 (merged 2026-05-11) removes the auto-purge in
on_del_edge that drove reachability oscillation in mixed
tincr/tinc-1.1pre18 meshes.
On top, pin Lassulus/tincr@a25eec05 which adds udp_addr clearing on
ENETUNREACH/EHOSTUNREACH/EAFNOSUPPORT/EADDRNOTAVAIL — without it,
IPv4-only hosts retry the same broken cached IPv6 udp_addr forever
when a peer's wire-advertised contact is v6 only. Submitted upstream
as Mic92/tincr#6.
Under load, the swaymsg get_tree subprocess could time out, the host
returned an empty windows list, and the extension treated that as "no
Firefox windows anywhere" — restoring a window for a workspace that
already had one. Distinguish query failure from a genuinely empty
result and skip restoration when state is unknown.
Upstream creates /var/lib/mautrix-discord at mode 0770 owned by
mautrix-discord:mautrix-discord, but only adds matrix-synapse to the
mautrix-discord-registration supplementary group. That left synapse
unable to traverse the directory to read discord-registration.yaml,
crashing matrix-synapse on startup.
tincr is wire-compatible with tinc 1.1pre18 (ExperimentalProtocol=yes,
Ed25519 only). Wrap the package with a /sbin -> bin symlink so
krebs.tinc's ${tincPackage}/sbin/tincd path resolves, and override
tincPackage. Existing peers all have Ed25519 keys; RSA keys in host
files are warned about and ignored.
The script was previously SSH_ASKPASS-only (raw PIN on stdout), which
broke rbw and gpg-agent: they speak the Assuan protocol over stdin/stdout
and could not parse the output ('failed to read password from pinentry').
Dispatch by argv:
- no args, or first arg starts with '-' (real pinentry flags like
--timeout 0 --ttyname ... --display ... --no-global-grab) -> Assuan mode
- bare prompt arg (SSH_ASKPASS convention used by ssh-tpm-agent)
-> raw PIN on stdout (existing behaviour)
Assuan mode handles SETTITLE/SETPROMPT/SETDESC/SETERROR/SETKEYINFO/OPTION/
RESET/NOP/BYE, percent-decodes incoming descriptions for the rofi -mesg,
and percent-encodes the D-line PIN.
Kernel-keyring caching is scoped to SSH_ASKPASS mode only; rbw and
gpg-agent do their own caching, and sharing one keyring slot across
unrelated secrets handed back the wrong PIN to whichever caller asked
second.
tools.kagi-search: default to 'rbw get kagi.com -f session_link'
Matches the actual vault layout (a kagi.com login with a session_link
field) instead of expecting a separate 'kagi-session-link' entry.
When gpg-agent is reachable and the user has a secret encryption
subkey ([E] ssb), emit the age-plugin-gpg default identity
(AGE-PLUGIN-GPG-1H40XPM, the bech32 of an empty body for the gpg
plugin). gpg-agent caches the passphrase / smartcard PIN, so this is
more frictionless than the yubikey path; we therefore place it above
yubikey/solo2 but below se/tpm.