Compare commits

..

5 Commits

Author SHA1 Message Date
084541da4c modules/programs: require manual definition; don't auto-populate attrset
this greatly decreases nix eval time
2024-02-28 13:32:52 +00:00
f7a82a845c sane.gui.phosh: remove 2024-02-28 13:32:52 +00:00
2bdef04552 nix-fast-build: fix to only populate it on supported platforms 2024-02-28 12:46:38 +00:00
2822a6f0dd import nix-fast-build
it's not really working on lappy (uses too much RAM), but maybe it'll help on desko
2024-02-28 12:37:50 +00:00
ab6e362f0c sane-wipe-browser: dont call pkill with sudo 2024-02-28 10:36:13 +00:00
202 changed files with 2043 additions and 5345 deletions

View File

@@ -15,9 +15,8 @@ the only hard dependency for my exported pkgs/modules should be [nixpkgs][nixpkg
building [hosts/](./hosts/) will require [sops][sops]. building [hosts/](./hosts/) will require [sops][sops].
you might specifically be interested in these files (elaborated further in #key-points-of-interest): you might specifically be interested in these files (elaborated further in #key-points-of-interest):
- ~~[`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)~~ - [`sxmo-utils`](./pkgs/additional/sxmo-utils/default.nix)
- ~~[example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)~~ - [example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)
- these files will remain until my config settles down, but i no longer use or maintain SXMO.
- [my implementation of impermanence](./modules/persist/default.nix) - [my implementation of impermanence](./modules/persist/default.nix)
- my way of deploying dotfiles/configuring programs per-user: - my way of deploying dotfiles/configuring programs per-user:
- [modules/fs/](./modules/fs/default.nix) - [modules/fs/](./modules/fs/default.nix)

15
TODO.md
View File

@@ -11,15 +11,16 @@
### sops/secrets ### sops/secrets
- rework secrets to leverage `sane.fs` - rework secrets to leverage `sane.fs`
- remove sops activation script as it's covered by my systemd sane.fs impl - remove sops activation script as it's covered by my systemd sane.fs impl
- user secrets could just use `gocryptfs`, like with ~/private?
- can gocryptfs support nested filesystems, each with different perms (for desko, moby, etc)?
### roles ### roles
- allow any host to take the role of `uninsane.org` - allow any host to take the role of `uninsane.org`
- will make it easier to test new services? - will make it easier to test new services?
### upstreaming ### upstreaming
- split out a sxmo module usable by NUR consumers
- bump nodejs version in lemmy-ui
- add updateScripts to all my packages in nixpkgs - add updateScripts to all my packages in nixpkgs
- fix lightdm-mobile-greeter for newer libhandy
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617> - REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
#### upstreaming to non-nixpkgs repos #### upstreaming to non-nixpkgs repos
@@ -46,8 +47,6 @@
- limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins - limit access to `~/knowledge/secrets` through an agent that requires GUI approval, so a firefox exploit can't steal all my logins
- port sane-sandboxed to a compiled language (hare?) - port sane-sandboxed to a compiled language (hare?)
- it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone. - it adds like 50-70ms launch time _on my laptop_. i'd hate to know how much that is on the pinephone.
- remove /run/wrappers from the sandbox path
- they're mostly useless when using no-new-privs, just an opportunity to forget to specify deps
- make dconf stuff less monolithic - make dconf stuff less monolithic
- i.e. per-app dconf profiles for those which need it. possible static config. - i.e. per-app dconf profiles for those which need it. possible static config.
- canaries for important services - canaries for important services
@@ -55,11 +54,6 @@
- integrate `nix check` into Gitea actions? - integrate `nix check` into Gitea actions?
### user experience ### user experience
- xdg-desktop-portal shouldn't kill children on exit
- *maybe* a job for `setsid -f`?
- replace starship prompt with something more efficient
- watch `forkstat`: it does way too much
- cleanup waybar so that it's not invoking playerctl every 2 seconds
- install apps: - install apps:
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/> - display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
- shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/> - shopping list (not in nixpkgs): <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
@@ -82,7 +76,6 @@
#### moby #### moby
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html> - fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
- moby: tune keyboard layout
- SwayNC: - SwayNC:
- don't show MPRIS if no players detected - don't show MPRIS if no players detected
- this is a problem of playerctld, i guess - this is a problem of playerctld, i guess
@@ -104,7 +97,6 @@
- RSS: integrate a paywall bypass - RSS: integrate a paywall bypass
- e.g. self-hosted [ladder](https://github.com/everywall/ladder) (like 12ft.io) - e.g. self-hosted [ladder](https://github.com/everywall/ladder) (like 12ft.io)
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig) - neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
- neovim: integrate LLMs
- Helix: make copy-to-system clipboard be the default - Helix: make copy-to-system clipboard be the default
- firefox/librewolf: persist history - firefox/librewolf: persist history
- just not cookies or tabs - just not cookies or tabs
@@ -122,6 +114,7 @@
### perf ### perf
- debug nixos-rebuild times - debug nixos-rebuild times
- i bet sane.programs adds a LOT of time, with how it automatically creates an attrs for EVERY package in nixpkgs.
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled - add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set - every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
- would be super handy for package prototyping! - would be super handy for package prototyping!

205
flake.lock generated
View File

@@ -1,76 +1,20 @@
{ {
"nodes": { "nodes": {
"flake-compat": {
"locked": {
"lastModified": 1688025799,
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
"owner": "nix-community",
"repo": "flake-compat",
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [
"nixpkgs-wayland",
"nix-eval-jobs",
"nixpkgs"
]
},
"locked": {
"lastModified": 1701473968,
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"lib-aggregate": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1710184940, "lastModified": 1698882062,
"narHash": "sha256-FzYm4td3FJfzOAuEkCXt3KdUgZuA072OAQXqIq+IAMo=", "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
"owner": "nix-community", "owner": "hercules-ci",
"repo": "lib-aggregate", "repo": "flake-parts",
"rev": "45b75bf534592c0c1c881a1c447f7fdb37a87eaf", "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "hercules-ci",
"repo": "lib-aggregate", "repo": "flake-parts",
"type": "github" "type": "github"
} }
}, },
@@ -91,56 +35,33 @@
"type": "github" "type": "github"
} }
}, },
"nix-eval-jobs": { "nix-fast-build": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"nix-github-actions": "nix-github-actions",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
"lastModified": 1705242886, "lastModified": 1703607026,
"narHash": "sha256-TLj334vRwFtSym3m+NnKcNCnKKPNoTC/TDZL40vmOso=", "narHash": "sha256-Emh0BPoqlS4ntp2UJrwydXfIP4qIMF0VBB2FUE3/M/E=",
"owner": "nix-community", "owner": "Mic92",
"repo": "nix-eval-jobs", "repo": "nix-fast-build",
"rev": "6b03a93296faf174b97546fd573c8b379f523a8d", "rev": "4376b8a33b217ee2f78ba3dcff01a3e464d13a46",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "Mic92",
"repo": "nix-eval-jobs", "repo": "nix-fast-build",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"nixpkgs-wayland",
"nix-eval-jobs",
"nixpkgs"
]
},
"locked": {
"lastModified": 1701208414,
"narHash": "sha256-xrQ0FyhwTZK6BwKhahIkUVZhMNk21IEI1nUcWSONtpo=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "93e39cc1a087d65bcf7a132e75a650c44dd2b734",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1703134684, "lastModified": 1698890957,
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=", "narHash": "sha256-DJ+SppjpPBoJr0Aro9TAcP3sxApCSieY6BYBCoWGUX8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523", "rev": "c082856b850ec60cda9f0a0db2bc7bd8900d708c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -152,26 +73,29 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1710031547, "dir": "lib",
"narHash": "sha256-pkUg3hOKuGWMGF9WEMPPN/G4pqqdbNGJQ54yhyQYDVY=", "lastModified": 1698611440,
"owner": "nix-community", "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"repo": "nixpkgs.lib", "owner": "NixOS",
"rev": "630ebdc047ca96d8126e16bb664c7730dc52f6e6", "repo": "nixpkgs",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "dir": "lib",
"repo": "nixpkgs.lib", "owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-next-unpatched": { "nixpkgs-next-unpatched": {
"locked": { "locked": {
"lastModified": 1710337169, "lastModified": 1708992120,
"narHash": "sha256-u2/74bhQuWykUZDWUIhHd6IpZiaQ0hSpTBbx0y9opkE=", "narHash": "sha256-t/8QV+lEroW5fK44w5oEUalIM0eYYVGs833AHDCIl4s=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4ee0840ba2ecc50458ab1677d108afcd691f4815", "rev": "6daf4de0662e1d895d220a4a4ddb356eb000abe9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -183,11 +107,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1710033658, "lastModified": 1708819810,
"narHash": "sha256-yiZiVKP5Ya813iYLho2+CcFuuHpaqKc/CoxOlANKcqM=", "narHash": "sha256-1KosU+ZFXf31GPeCBNxobZWMgHsSOJcrSFA6F2jhzdE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b17375d3bb7c79ffc52f3538028b2ec06eb79ef8", "rev": "89a2a12e6c8c6a56c72eb3589982c8e2f89c70ea",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -199,11 +123,11 @@
}, },
"nixpkgs-unpatched": { "nixpkgs-unpatched": {
"locked": { "locked": {
"lastModified": 1710339354, "lastModified": 1708995544,
"narHash": "sha256-+P5ccUPiLouHexb8aJrUOVOIja9qm+fG57pgAu7uIRs=", "narHash": "sha256-YJgLopKOKVTggnKzjX4OiAS22hx/vNv397DcsAyTZgY=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "2dbc8f62d8af7a1ab962e4b20d12b25ddcb86ced", "rev": "5bd8df40204f47a12263f3614c72cd5b6832a9a0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -213,35 +137,12 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-wayland": {
"inputs": {
"flake-compat": "flake-compat",
"lib-aggregate": "lib-aggregate",
"nix-eval-jobs": "nix-eval-jobs",
"nixpkgs": [
"nixpkgs-unpatched"
]
},
"locked": {
"lastModified": 1710317949,
"narHash": "sha256-bwReMiWPA2wYBvKEMhO8pJcu+o+7ocy5hGkSoawTHu0=",
"owner": "nix-community",
"repo": "nixpkgs-wayland",
"rev": "771cb198c281db6918829651f194bf4db32e342d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs-wayland",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"mobile-nixos": "mobile-nixos", "mobile-nixos": "mobile-nixos",
"nix-fast-build": "nix-fast-build",
"nixpkgs-next-unpatched": "nixpkgs-next-unpatched", "nixpkgs-next-unpatched": "nixpkgs-next-unpatched",
"nixpkgs-unpatched": "nixpkgs-unpatched", "nixpkgs-unpatched": "nixpkgs-unpatched",
"nixpkgs-wayland": "nixpkgs-wayland",
"sops-nix": "sops-nix", "sops-nix": "sops-nix",
"uninsane-dot-org": "uninsane-dot-org" "uninsane-dot-org": "uninsane-dot-org"
} }
@@ -254,11 +155,11 @@
"nixpkgs-stable": "nixpkgs-stable" "nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1710195194, "lastModified": 1708987867,
"narHash": "sha256-KFxCJp0T6TJOz1IOKlpRdpsCr9xsvlVuWY/VCiAFnTE=", "narHash": "sha256-k2lDaDWNTU5sBVHanYzjDKVDmk29RHIgdbbXu5sdzBA=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "e52d8117b330f690382f1d16d81ae43daeb4b880", "rev": "a1c8de14f60924fafe13aea66b46157f0150f4cf",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -267,35 +168,19 @@
"type": "github" "type": "github"
} }
}, },
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": { "treefmt-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nixpkgs-wayland", "nix-fast-build",
"nix-eval-jobs",
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1702979157, "lastModified": 1698438538,
"narHash": "sha256-RnFBbLbpqtn4AoJGXKevQMCGhra4h6G2MPcuTSZZQ+g=", "narHash": "sha256-AWxaKTDL3MtxaVTVU5lYBvSnlspOS0Fjt8GxBgnU0Do=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "2961375283668d867e64129c22af532de8e77734", "rev": "5deb8dc125a9f83b65ca86cf0c8167c46593e0b1",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -48,11 +48,6 @@
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next"; # nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-staging-next";
nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next"; nixpkgs-next-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
nixpkgs-wayland = {
url = "github:nix-community/nixpkgs-wayland";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
};
mobile-nixos = { mobile-nixos = {
# <https://github.com/nixos/mobile-nixos> # <https://github.com/nixos/mobile-nixos>
# only used for building disk images, not relevant after deployment # only used for building disk images, not relevant after deployment
@@ -62,6 +57,10 @@
url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b"; url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b";
flake = false; flake = false;
}; };
nix-fast-build = {
# https://github.com/Mic92/nix-fast-build
url = "github:Mic92/nix-fast-build";
};
sops-nix = { sops-nix = {
# <https://github.com/Mic92/sops-nix> # <https://github.com/Mic92/sops-nix>
# used to distribute secrets to my hosts # used to distribute secrets to my hosts
@@ -81,8 +80,8 @@
self, self,
nixpkgs-unpatched, nixpkgs-unpatched,
nixpkgs-next-unpatched ? nixpkgs-unpatched, nixpkgs-next-unpatched ? nixpkgs-unpatched,
nixpkgs-wayland,
mobile-nixos, mobile-nixos,
nix-fast-build,
sops-nix, sops-nix,
uninsane-dot-org, uninsane-dot-org,
... ...
@@ -103,7 +102,30 @@
patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs { patchNixpkgs = variant: nixpkgs: (import ./nixpatches/flake.nix).outputs {
inherit variant nixpkgs; inherit variant nixpkgs;
self = patchNixpkgs variant nixpkgs; self = patchNixpkgs variant nixpkgs;
}; } // {
# sourceInfo includes fields (square brackets for the ones which are not always present):
# - [dirtyRev]
# - [dirtyShortRev]
# - lastModified
# - lastModifiedDate
# - narHash
# - outPath
# - [rev]
# - [revCount]
# - [shortRev]
# - submodules
#
# these values are used within nixpkgs:
# - to give a friendly name to the nixos system (`readlink /run/current-system` -> `...nixos-system-desko-24.05.20240227.dirty`)
# - to alias `import <nixpkgs>` so that nix uses the system's nixpkgs when called externally (supposedly).
#
# these values seem to exist both within the `sourceInfo` attrset and at the top-level.
# for a list of all implicit flake outputs (which is what these seem to be):
# $ nix-repl
# > lf .
# > <tab>
inherit (self) sourceInfo;
} // self.sourceInfo;
nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched; nixpkgs' = patchNixpkgs "master" nixpkgs-unpatched;
nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}"; nixpkgsCompiledBy = system: nixpkgs'.legacyPackages."${system}";
@@ -190,18 +212,12 @@
let let
mobile = (import "${mobile-nixos}/overlay/overlay.nix"); mobile = (import "${mobile-nixos}/overlay/overlay.nix");
uninsane = uninsane-dot-org.overlays.default; uninsane = uninsane-dot-org.overlays.default;
wayland = final: prev: { # TODO: why do i have to use `self.inputs.nix-fast-build` instead of just `nix-fast-build` here?
# default is to dump the packages into `waylandPkgs` *and* the toplevel. nix-fast-build = (_: prev: self.inputs.nix-fast-build.packages."${prev.stdenv.system}" or {});
# but i just want the `waylandPkgs` set
inherit (nixpkgs-wayland.overlays.default final prev)
waylandPkgs
new-wayland-protocols #< 2024/03/10: nixpkgs-wayland assumes this will be in the toplevel
;
};
in in
(mobile final prev) (mobile final prev)
// (nix-fast-build final prev)
// (uninsane final prev) // (uninsane final prev)
// (wayland final prev)
; ;
}; };
@@ -257,8 +273,6 @@
pkgs = self.legacyPackages."x86_64-linux"; pkgs = self.legacyPackages."x86_64-linux";
sanePkgs = import ./pkgs { inherit pkgs; }; sanePkgs = import ./pkgs { inherit pkgs; };
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" '' deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
set -e
host="${host}" host="${host}"
addr="${addr}" addr="${addr}"
action="${if action != null then action else ""}" action="${if action != null then action else ""}"
@@ -272,8 +286,8 @@
fi fi
} }
nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./build/result-$host" "$@" nix build ".#nixosConfigurations.$host.config.system.build.toplevel" --out-link "./result-$host" "$@"
storePath="$(readlink ./build/result-$host)" storePath="$(readlink ./result-$host)"
# mimic `nixos-rebuild --target-host`, in effect: # mimic `nixos-rebuild --target-host`, in effect:
# - nix-copy-closure ... # - nix-copy-closure ...
@@ -376,8 +390,6 @@
- or `nix run '.#preDeploy'` to target all hosts - or `nix run '.#preDeploy'` to target all hosts
- `nix run '.#check'` - `nix run '.#check'`
- make sure all systems build; NUR evaluates - make sure all systems build; NUR evaluates
- `nix run '.#bench'`
- benchmark the eval time of common targets this flake provides
specific build targets of interest: specific build targets of interest:
- `nix build '.#imgs.rescue'` - `nix build '.#imgs.rescue'`
@@ -521,7 +533,6 @@
--option allow-import-from-derivation true \ --option allow-import-from-derivation true \
--drv-path --show-trace \ --drv-path --show-trace \
-I nixpkgs=${nixpkgs-unpatched} \ -I nixpkgs=${nixpkgs-unpatched} \
-I nixpkgs-overlays=${./.}/hosts/common/nix/overlay \
-I ../../ \ -I ../../ \
| tee # tee to prevent interactive mode | tee # tee to prevent interactive mode
''); '');
@@ -533,7 +544,7 @@
checkHost = host: let checkHost = host: let
shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host; shellHost = pkgs.lib.replaceStrings [ "-" ] [ "_" ] host;
in '' in ''
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./build/result-${host} -j2 "$@" nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} -j2 "$@"
RC_${shellHost}=$? RC_${shellHost}=$?
''; '';
in builtins.toString (pkgs.writeShellScript in builtins.toString (pkgs.writeShellScript
@@ -582,31 +593,7 @@
check.rescue = { check.rescue = {
type = "app"; type = "app";
program = builtins.toString (pkgs.writeShellScript "check-rescue" '' program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
nix build -v '.#imgs.rescue' --out-link ./build/result-rescue-img -j2 nix build -v '.#imgs.rescue' --out-link ./result-rescue-img -j2
'');
};
bench = {
type = "app";
program = builtins.toString (pkgs.writeShellScript "bench" ''
doBench() {
attrPath="$1"
shift
echo -n "benchmarking eval of '$attrPath'... "
/run/current-system/sw/bin/time -f "%e sec" -o /dev/stdout \
nix eval --no-eval-cache --quiet --raw ".#$attrPath" --apply 'result: if result != null then "" else "unexpected null"' $@ 2> /dev/null
}
if [ -n "$1" ]; then
doBench "$@"
else
doBench hostConfigs
doBench hostConfigs.lappy
doBench hostConfigs.lappy.sane.programs
doBench hostConfigs.lappy.sane.users.colin
doBench hostConfigs.lappy.sane.fs
doBench hostConfigs.lappy.environment.systemPackages
fi
''); '');
}; };
}; };

View File

@@ -2,6 +2,7 @@
{ {
imports = [ imports = [
./fs.nix ./fs.nix
./polyfill.nix
]; ];
sane.roles.client = true; sane.roles.client = true;

View File

@@ -0,0 +1,41 @@
# doesn't actually *enable* anything,
# but sets up any modules such that if they *were* enabled, they'll act as expected.
{ pkgs, ... }:
{
sane.gui.sxmo = {
noidle = true; #< power button requires 1s hold, which makes it impractical to be dealing with.
settings = {
# XXX: make sure the user is part of the `input` group!
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-id/usb-Wacom_Co._Ltd._Pen_and_multitouch_sensor-event-if00";
# these identifiers are from `swaymsg -t get_inputs`
SXMO_VOLUME_BUTTON = "1:1:AT_Translated_Set_2_keyboard";
# SXMO_VOLUME_BUTTON = "none";
# N.B.: thinkpad's power button requires a full second press to do anything
SXMO_POWER_BUTTON = "0:1:Power_Button";
# SXMO_POWER_BUTTON = "none";
SXMO_DISABLE_LEDS = "1";
SXMO_UNLOCK_IDLE_TIME = "120"; # default
# sxmo tries to determine device type from /proc/device-tree/compatible,
# but that doesn't seem to exist on NixOS? (or maybe it just doesn't exist
# on non-aarch64 builds).
# the device type informs (at least):
# - SXMO_WIFI_MODULE
# - SXMO_RTW_SCAN_INTERVAL
# - SXMO_TOUCHSCREEN_ID
# - SXMO_MONITOR
# - SXMO_ALSA_CONTROL_NAME
# - SXMO_SWAY_SCALE
# see <repo:mil/sxmo-utils:scripts/deviceprofiles>
# SXMO_DEVICE_NAME = "pine64,pinephone-1.2";
# if sxmo doesn't know the device, it can't decide whether to use one_button or three_button mode
# and so it just wouldn't handle any button inputs (sxmo_hook_inputhandler.sh not on path)
SXMO_DEVICE_NAME = "three_button_touchscreen";
};
package = (pkgs.sxmo-utils.override { preferSystemd = true; }).overrideAttrs (base: {
postPatch = (base.postPatch or "") + ''
# after volume-button navigation mode, restore full keyboard functionality
cp ${./xkb_mobile_normal_buttons} ./configs/xkb/xkb_mobile_normal_buttons
'';
});
};
}

View File

@@ -36,15 +36,10 @@
sops.secrets.colin-passwd.neededForUsers = true; sops.secrets.colin-passwd.neededForUsers = true;
# sane.gui.sxmo.enable = true; sane.gui.sxmo.enable = true;
sane.programs.sway.enableFor.user.colin = true; # sane.programs.consoleUtils.enableFor.user.colin = false;
sane.programs.swaylock.enableFor.user.colin = false; #< not usable on touch # sane.programs.guiApps.enableFor.user.colin = false;
sane.programs.schlock.enableFor.user.colin = true;
sane.programs.swayidle.config.actions.screenoff.delay = 300;
sane.programs.swayidle.config.actions.screenoff.enable = true;
sane.programs.sane-input-handler.enableFor.user.colin = true;
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile! sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
sane.programs.fcitx5.enableFor.user.colin = false; # does not cross compile
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
sane.programs.nvme-cli.enableFor.system = false; # does not cross compile (libhugetlbfs) sane.programs.nvme-cli.enableFor.system = false; # does not cross compile (libhugetlbfs)
@@ -57,6 +52,7 @@
# sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping. # sane.programs.signal-desktop.config.autostart = true; # TODO: enable once electron stops derping.
# sane.programs."gnome.geary".config.autostart = true; # sane.programs."gnome.geary".config.autostart = true;
# sane.programs.calls.config.autostart = true; # sane.programs.calls.config.autostart = true;
sane.programs.mpv.config.vo = "wlshm"; #< see hosts/common/programs/mpv.nix for details
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority` # HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
@@ -128,12 +124,42 @@
# enable rotation sensor # enable rotation sensor
hardware.sensor.iio.enable = true; hardware.sensor.iio.enable = true;
# TODO: move elsewhere... # inject specialized alsa configs via the environment.
systemd.services.ModemManager.serviceConfig = { # specifically, this gets the pinephone headphones & internal earpiece working.
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored # see pkgs/patched/alsa-ucm-conf for more info.
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ]; environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
# --debug sets DEBUG level logging: so reset environment.pathsToLink = [ "/share/alsa/ucm2" ];
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ]; environment.systemPackages = [
(pkgs.alsa-ucm-conf-sane.override {
# internal speaker has a tendency to break :(
preferEarpiece = true;
})
];
systemd = let
ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
in {
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
# pipewire
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
# pulseaudio
# user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
# services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
# TODO: move elsewhere...
services.ModemManager.serviceConfig = {
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
# --debug sets DEBUG level logging: so reset
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
};
}; };
services.udev.extraRules = let services.udev.extraRules = let

View File

@@ -85,7 +85,6 @@ in
"lima.sched_timeout_ms=2000" "lima.sched_timeout_ms=2000"
]; ];
# services.xserver.displayManager.job.preStart = ensureHWReady; services.xserver.displayManager.job.preStart = ensureHWReady;
# systemd.services.greetd.preStart = ensureHWReady; systemd.services.greetd.preStart = ensureHWReady;
systemd.services.unl0kr.preStart = ensureHWReady;
} }

View File

@@ -24,22 +24,73 @@
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
}; };
sane.programs.alacritty.config.fontSize = 9; sane.gui.sxmo = {
nogesture = true;
settings = {
### hardware: touch screen
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
# vol and power are detected correctly by upstream
sane.programs.sway.config = { ### preferences
font = "pango:monospace 10"; DEFAULT_COUNTRY = "US";
mod = "Mod1"; # prefer Alt
workspace_layout = "tabbed";
};
sane.programs.waybar.config = { SXMO_AUTOROTATE = "1"; # enable auto-rotation at launch. has no meaning in stock/upstream sxmo-utils
fontSize = 14;
height = 26; # BEMENU lines (wayland DMENU):
persistWorkspaces = [ "1" "2" "3" "4" "5" ]; # - camera is 9th entry
modules.media = false; # - flashlight is 10th entry
modules.network = false; # - config is 14th entry. inside that:
modules.perf = false; # - autorotate is 11th entry
modules.windowTitle = false; # - system menu is 19th entry
# TODO: show modem state # - close is 20th entry
# - power is 15th entry
# - close is 16th entry
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
# gravity: how far to tilt the device before the screen rotates
# for a given setting, normal <-> invert requires more movement then left <-> right
# i.e. the settingd doesn't feel completely symmetric
# SXMO_ROTATION_GRAVITY default is 16374
# SXMO_ROTATION_GRAVITY = "12800"; # uncomfortably high
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
SXMO_ROTATION_GRAVITY = "12000";
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
# sway/wayland scaling:
# - conflicting info out there on how scaling actually works
# at the least, for things where it matters (mpv), it seems like scale settings have 0 effect on perf
# ways to enforce scaling:
# - <https://wiki.archlinux.org/title/HiDPI>
# - `swaymsg -- output DSI-1 scale 2.0` (scales everything)
# - `dconf write /org/gnome/desktop/interface/text-scaling-factor 2.0` (scales ONLY TEXT)
# - `GDK_DPI_SCALE=2.0` (scales ONLY TEXT)
#
# application notes:
# - cozy: in landscape, playback position is not visible unless scale <= 1.7
# - if in a tab, then scale 1.6 is the max
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
SXMO_SWAY_SCALE = "1.6";
# SXMO_SWAY_SCALE = "1.8";
# SXMO_SWAY_SCALE = "2";
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
# wvkbd layers:
# - full
# - landscape
# - special (e.g. coding symbols like ~)
# - emoji
# - nav
# - simple (like landscape, but no parens/tab/etc; even fewer chars)
# - simplegrid (simple, but grid layout)
# - dialer (digits)
# - cyrillic
# - arabic
# - persian
# - greek
# - georgian
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
WVKBD_LAYERS = "full,special,emoji";
};
}; };
} }

View File

@@ -6,7 +6,7 @@
boot.loader.efi.canTouchEfiVariables = false; boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ]; sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.persist.enable = false; # what we mean here is that the image is immutable; `/` is still tmpfs. sane.persist.enable = false;
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
# auto-login at shell # auto-login at shell

View File

@@ -82,34 +82,34 @@
}; };
sane.fs."/mnt/usb-hdd".mount = {}; sane.fs."/mnt/usb-hdd".mount = {};
sane.persist.sys.byStore.plaintext = [{ sane.persist.sys.byStore.plaintext = [
path = "/var/media"; # TODO: this is overly broad; only need media and share directories to be persisted
method = "bind"; #< this HAS to be `bind` if we're going to persist the whole thing but create subdirs, as below. { user = "colin"; group = "users"; path = "/var/lib/uninsane"; method = "bind"; }
user = "colin"; ];
group = "media"; # force some problematic directories to always get correct permissions:
mode = "0755"; sane.fs."/var/lib/uninsane/media".dir.acl = {
}]; user = "colin"; group = "media"; mode = "0775";
sane.fs."/var/media/archive".dir = {}; };
sane.fs."/var/lib/uninsane/media/archive".dir = {};
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path) # this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
sane.fs."/var/media/archive/README.md".file.text = '' sane.fs."/var/lib/uninsane/media/archive/README.md".file.text = ''
this directory is for media i wish to remove from my library, this directory is for media i wish to remove from my library,
but keep for a short time in case i reverse my decision. but keep for a short time in case i reverse my decision.
treat it like a system trash can. treat it like a system trash can.
''; '';
sane.fs."/var/media/Books".dir = {}; sane.fs."/var/lib/uninsane/media/Books".dir = {};
sane.fs."/var/media/Books/Audiobooks".dir = {}; sane.fs."/var/lib/uninsane/media/Books/Audiobooks".dir = {};
sane.fs."/var/media/Books/Books".dir = {}; sane.fs."/var/lib/uninsane/media/Books/Books".dir = {};
sane.fs."/var/media/Books/Visual".dir = {}; sane.fs."/var/lib/uninsane/media/Books/Visual".dir = {};
sane.fs."/var/media/collections".dir = {}; sane.fs."/var/lib/uninsane/media/collections".dir = {};
sane.fs."/var/media/datasets".dir = {}; sane.fs."/var/lib/uninsane/media/datasets".dir = {};
sane.fs."/var/media/freeleech".dir = {}; sane.fs."/var/lib/uninsane/media/freeleech".dir = {};
sane.fs."/var/media/Music".dir = {}; sane.fs."/var/lib/uninsane/media/Music".dir = {};
sane.fs."/var/media/Pictures".dir = {}; sane.fs."/var/lib/uninsane/media/Pictures".dir = {};
sane.fs."/var/media/Videos".dir = {}; sane.fs."/var/lib/uninsane/media/Videos".dir = {};
sane.fs."/var/media/Videos/Film".dir = {}; sane.fs."/var/lib/uninsane/media/Videos/Film".dir = {};
sane.fs."/var/media/Videos/Shows".dir = {}; sane.fs."/var/lib/uninsane/media/Videos/Shows".dir = {};
sane.fs."/var/media/Videos/Talks".dir = {}; sane.fs."/var/lib/uninsane/media/Videos/Talks".dir = {};
# this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path) # this is file.text instead of symlink.text so that it may be read over a remote mount (where consumers might not have any /nix/store/.../README.md path)
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = '' sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
this directory may seem redundant with ../media/datasets. it isn't. this directory may seem redundant with ../media/datasets. it isn't.
@@ -122,19 +122,19 @@
user = "colin"; user = "colin";
group = "users"; group = "users";
mode = "0777"; mode = "0777";
path = "/var/media/Videos"; path = "/var/lib/uninsane/media/Videos";
} }
{ {
user = "colin"; user = "colin";
group = "users"; group = "users";
mode = "0777"; mode = "0777";
path = "/var/media/freeleech"; path = "/var/lib/uninsane/media/freeleech";
} }
{ {
user = "colin"; user = "colin";
group = "users"; group = "users";
mode = "0775"; mode = "0777";
path = "/var/lib/uninsane/datasets"; path = "/var/lib/uninsane/media/datasets";
} }
]; ];

View File

@@ -9,7 +9,7 @@
fileSystems."/var/export/media" = { fileSystems."/var/export/media" = {
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction) # everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
device = "/var/media"; device = "/var/lib/uninsane/media";
options = [ "rbind" ]; options = [ "rbind" ];
}; };
# fileSystems."/var/export/playground" = { # fileSystems."/var/export/playground" = {

View File

@@ -1,6 +1,5 @@
{ lib, ... }: { lib, ... }:
lib.mkIf false #< i don't actively use navidrome
{ {
sane.persist.sys.byStore.plaintext = [ sane.persist.sys.byStore.plaintext = [
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; method = "bind"; } { user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; method = "bind"; }
@@ -10,7 +9,7 @@ lib.mkIf false #< i don't actively use navidrome
# docs: https://www.navidrome.org/docs/usage/configuration-options/ # docs: https://www.navidrome.org/docs/usage/configuration-options/
Address = "127.0.0.1"; Address = "127.0.0.1";
Port = 4533; Port = 4533;
MusicFolder = "/var/media/Music"; MusicFolder = "/var/lib/uninsane/media/Music";
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded"; CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
AutoImportPlaylists = false; AutoImportPlaylists = false;
ScanSchedule = "@every 1h"; ScanSchedule = "@every 1h";

View File

@@ -55,8 +55,8 @@ in
# web blog/personal site # web blog/personal site
# alternative way to link stuff into the share: # alternative way to link stuff into the share:
# sane.fs."/var/www/sites/uninsane.org/share/Ubunchu".mount.bind = "/var/media/Books/Visual/HiroshiSeo/Ubunchu"; # sane.fs."/var/lib/uninsane/share/Ubunchu".mount.bind = "/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu";
# sane.fs."/var/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {}; # sane.fs."/var/lib/uninsane/media/Books/Visual/HiroshiSeo/Ubunchu".dir = {};
services.nginx.virtualHosts."uninsane.org" = publog { services.nginx.virtualHosts."uninsane.org" = publog {
# a lot of places hardcode https://uninsane.org, # a lot of places hardcode https://uninsane.org,
# and then when we mix http + non-https, we get CORS violations # and then when we mix http + non-https, we get CORS violations

View File

@@ -44,13 +44,13 @@
# [Alias]/path/on/disk # [Alias]/path/on/disk
# NOTE: Music library is quick to scan; videos take a solid 10min to scan. # NOTE: Music library is quick to scan; videos take a solid 10min to scan.
# TODO: re-enable the other libraries # TODO: re-enable the other libraries
# "[Audioooks]/var/media/Books/Audiobooks" # "[Audioooks]/var/lib/uninsane/media/Books/Audiobooks"
# "[Books]/var/media/Books/Books" # "[Books]/var/lib/uninsane/media/Books/Books"
# "[Manga]/var/media/Books/Visual" # "[Manga]/var/lib/uninsane/media/Books/Visual"
# "[games]/var/media/games" # "[games]/var/lib/uninsane/media/games"
"[Music]/var/media/Music" "[Music]/var/lib/uninsane/media/Music"
# "[Film]/var/media/Videos/Film" # "[Film]/var/lib/uninsane/media/Videos/Film"
# "[Shows]/var/media/Videos/Shows" # "[Shows]/var/lib/uninsane/media/Videos/Shows"
]; ];
# directories.downloads = "..." # TODO # directories.downloads = "..." # TODO
# directories.incomplete = "..." # TODO # directories.incomplete = "..." # TODO

View File

@@ -72,8 +72,8 @@ in
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG # see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG
anti-brute-force-enabled = false; anti-brute-force-enabled = false;
download-dir = "/var/media"; download-dir = "/var/lib/uninsane/media";
incomplete-dir = "/var/media/incomplete"; incomplete-dir = "/var/lib/uninsane/media/incomplete";
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable: # transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
# TODO: uncomment this line! # TODO: uncomment this line!
incomplete-dir-enabled = false; incomplete-dir-enabled = false;

View File

@@ -43,6 +43,17 @@
fi fi
''; '';
}; };
system.activationScripts.notifyActive = {
text = ''
# send a notification to any sway users logged in, that the system has been activated/upgraded.
# this probably doesn't work if more than one sway session exists on the system.
_notifyActiveSwaySock="$(echo /run/user/*/sway-ipc*.sock)"
if [ -e "$_notifyActiveSwaySock" ]; then
SWAYSOCK="$_notifyActiveSwaySock" ${config.sane.programs.sway.packageUnwrapped}/bin/swaymsg -- exec \
"${pkgs.libnotify}/bin/notify-send 'nixos activated' 'version: $(cat $systemConfig/nixos-version)'"
fi
'';
};
# link debug symbols into /run/current-system/sw/lib/debug # link debug symbols into /run/current-system/sw/lib/debug
# hopefully picked up by gdb automatically? # hopefully picked up by gdb automatically?

View File

@@ -121,7 +121,6 @@ let
texts = [ texts = [
(fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following> (fromDb "acoup.blog/feed") # history, states. author: <https://historians.social/@bretdevereaux/following>
(fromDb "amosbbatto.wordpress.com" // tech) (fromDb "amosbbatto.wordpress.com" // tech)
(fromDb "anish.lakhwara.com" // tech)
(fromDb "applieddivinitystudies.com" // rat) (fromDb "applieddivinitystudies.com" // rat)
(fromDb "artemis.sh" // tech) (fromDb "artemis.sh" // tech)
(fromDb "ascii.textfiles.com" // tech) # Jason Scott (fromDb "ascii.textfiles.com" // tech) # Jason Scott
@@ -162,7 +161,6 @@ let
(fromDb "palladiummag.com" // uncat) (fromDb "palladiummag.com" // uncat)
(fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im (fromDb "philosopher.coach" // rat) # Peter Saint-Andre -- side project of stpeter.im
(fromDb "pomeroyb.com" // tech) (fromDb "pomeroyb.com" // tech)
(fromDb "postmarketos.org/blog" // tech)
(fromDb "preposterousuniverse.com" // rat) # Sean Carroll (fromDb "preposterousuniverse.com" // rat) # Sean Carroll
(fromDb "profectusmag.com" // uncat) (fromDb "profectusmag.com" // uncat)
(fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny (fromDb "project-insanity.org" // tech) # shared blog by a few NixOS devs, notably onny
@@ -174,11 +172,9 @@ let
(fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary) (fromDb "sagacioussuricata.com" // tech) # ian (Sanctuary)
(fromDb "semiaccurate.com" // tech) (fromDb "semiaccurate.com" // tech)
(fromDb "sideways-view.com" // rat) # Paul Christiano (fromDb "sideways-view.com" // rat) # Paul Christiano
(fromDb "slatecave.net" // tech)
(fromDb "slimemoldtimemold.com" // rat) (fromDb "slimemoldtimemold.com" // rat)
(fromDb "spectrum.ieee.org" // tech) (fromDb "spectrum.ieee.org" // tech)
(fromDb "stpeter.im/atom.xml" // pol) (fromDb "stpeter.im/atom.xml" // pol)
(fromDb "thediff.co" // pol) # Byrne Hobart
# (fromDb "theregister.com" // tech) # (fromDb "theregister.com" // tech)
(fromDb "thisweek.gnome.org" // tech) (fromDb "thisweek.gnome.org" // tech)
(fromDb "tuxphones.com" // tech) (fromDb "tuxphones.com" // tech)
@@ -186,15 +182,17 @@ let
(fromDb "unintendedconsequenc.es" // rat) (fromDb "unintendedconsequenc.es" // rat)
# (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo # (fromDb "vitalik.ca" // tech) # moved to vitalik.eth.limo
(fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin (fromDb "vitalik.eth.limo" // tech) # Vitalik Buterin
# (fromDb "webcurious.co.uk" // uncat) # link aggregator; defunct? (fromDb "webcurious.co.uk" // uncat)
(fromDb "xn--gckvb8fzb.com" // tech) (fromDb "xn--gckvb8fzb.com" // tech)
(mkSubstack "astralcodexten" // rat // daily) # Scott Alexander (mkSubstack "astralcodexten" // rat // daily) # Scott Alexander
(mkSubstack "byrnehobart" // pol // infrequent)
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled # (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
(mkSubstack "eliqian" // rat // weekly) (mkSubstack "eliqian" // rat // weekly)
(mkSubstack "oversharing" // pol // daily) (mkSubstack "oversharing" // pol // daily)
(mkSubstack "samkriss" // humor // infrequent) (mkSubstack "samkriss" // humor // infrequent)
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly) (mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
(mkText "http://boginjr.com/feed" // tech // infrequent) (mkText "http://boginjr.com/feed" // tech // infrequent)
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/> (mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent) #quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent) # (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
(mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans (mkText "https://jvns.ca/atom.xml" // tech // weekly) # Julia Evans
@@ -213,7 +211,6 @@ let
(fromDb "youtube.com/@Exurb1a") (fromDb "youtube.com/@Exurb1a")
(fromDb "youtube.com/@hbomberguy") (fromDb "youtube.com/@hbomberguy")
(fromDb "youtube.com/@JackStauber") (fromDb "youtube.com/@JackStauber")
(fromDb "youtube.com/@NativLang")
(fromDb "youtube.com/@PolyMatter") (fromDb "youtube.com/@PolyMatter")
# (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann # (fromDb "youtube.com/@rossmanngroup" // pol // tech) # Louis Rossmann
(fromDb "youtube.com/@TechnologyConnections" // tech) (fromDb "youtube.com/@TechnologyConnections" // tech)

View File

@@ -79,6 +79,11 @@
HandleLidSwitch=lock HandleLidSwitch=lock
''; '';
# some packages build only if binfmt *isn't* present
nix.settings.system-features = lib.mkIf (config.boot.binfmt.emulatedSystems == []) [
"no-binfmt"
];
# services.snapper.configs = { # services.snapper.configs = {
# root = { # root = {
# subvolume = "/"; # subvolume = "/";

View File

@@ -1,4 +1,4 @@
{ config, lib, ... }: { config, ... }:
{ {
sane.user.persist.byStore.plaintext = [ sane.user.persist.byStore.plaintext = [
"archive" "archive"
@@ -29,17 +29,14 @@
]; ];
# convenience # convenience
sane.user.fs = let sane.user.fs.".persist/private".symlink.target = config.sane.persist.stores.private.origin;
persistEnabled = config.sane.persist.enable; sane.user.fs.".persist/plaintext".symlink.target = config.sane.persist.stores.plaintext.origin;
in { sane.user.fs.".persist/ephemeral".symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin;
".persist/private" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.private.origin; };
".persist/plaintext" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.plaintext.origin; };
".persist/ephemeral" = lib.mkIf persistEnabled { symlink.target = config.sane.persist.stores.cryptClearOnBoot.origin; };
"nixos".symlink.target = "dev/nixos"; sane.user.fs."nixos".symlink.target = "dev/nixos";
"Books/servo".symlink.target = "/mnt/servo/media/Books"; sane.user.fs."Books/servo".symlink.target = "/mnt/servo/media/Books";
"Videos/servo".symlink.target = "/mnt/servo/media/Videos"; sane.user.fs."Videos/servo".symlink.target = "/mnt/servo/media/Videos";
"Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros"; # sane.user.fs."Music/servo".symlink.target = "/mnt/servo/media/Music";
}; sane.user.fs."Pictures/servo-macros".symlink.target = "/mnt/servo/media/Pictures/macros";
} }

View File

@@ -53,9 +53,8 @@
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages. # allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages.
# this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`. # this is actually a no-op, and the real action happens in assigning `nix.settings.nix-path`.
nix.nixPath = (lib.optionals config.sane.enableSlowPrograms [ nix.nixPath = [
"nixpkgs=${pkgs.path}" "nixpkgs=${pkgs.path}"
]) ++ [
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root # note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
# "nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay" # "nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
# as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git # as long as my system itself doesn't rely on NIXPKGS at runtime, we can point the overlays to git
@@ -64,13 +63,7 @@
]; ];
# ensure new deployments have a source of this repo with which they can bootstrap. # ensure new deployments have a source of this repo with which they can bootstrap.
# this however changes on every commit and can be slow to copy for e.g. `moby`. environment.etc."nixos".source = ../../..;
environment.etc."nixos" = lib.mkIf config.sane.enableSlowPrograms {
source = ../../..;
};
environment.etc."nix/registry.json" = lib.mkIf (!config.sane.enableSlowPrograms) {
enable = false;
};
systemd.services.nix-daemon.serviceConfig = { systemd.services.nix-daemon.serviceConfig = {
# the nix-daemon manages nix builders # the nix-daemon manages nix builders

View File

@@ -13,7 +13,7 @@
"/run/current-system/sw" "/run/current-system/sw"
]; ];
# NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix" in <nixos/modules/programs/environment.nix>. # NIXPKGS_CONFIG defaults to "/etc/nix/nixpkgs-config.nix", for idfk why.
# that's never existed on my system and everything does fine without it set empty (no nixpkgs API to forcibly *unset* it). # that's never existed on my system and everything does fine without it set empty (no nixpkgs API to forcibly *unset* it).
environment.variables.NIXPKGS_CONFIG = lib.mkForce ""; environment.variables.NIXPKGS_CONFIG = lib.mkForce "";
# XDG_CONFIG_DIRS defaults to "/etc/xdg", which doesn't exist. # XDG_CONFIG_DIRS defaults to "/etc/xdg", which doesn't exist.
@@ -42,26 +42,4 @@
# so as to inform when trying to run a non-nixos binary? # so as to inform when trying to run a non-nixos binary?
# IMO that's confusing: i thought /lib/ld-linux.so was some file actually required by nix. # IMO that's confusing: i thought /lib/ld-linux.so was some file actually required by nix.
environment.stub-ld.enable = false; environment.stub-ld.enable = false;
# `less.enable` sets LESSKEYIN_SYSTEM, LESSOPEN, LESSCLOSE env vars, which does confusing "lesspipe" things, so disable that.
# it's enabled by default from `<nixos/modules/programs/environment.nix>`, who also sets `PAGER="less"` and `EDITOR="nano"` (keep).
programs.less.enable = lib.mkForce false;
environment.variables.PAGER = lib.mkOverride 900 ""; # mkDefault sets 1000. non-override is 100. 900 will beat the nixpkgs `mkDefault` but not anyone else.
environment.variables.EDITOR = lib.mkOverride 900 "";
# several packages (dconf, modemmanager, networkmanager, gvfs, polkit, udisks, bluez/blueman, feedbackd, etc)
# will add themselves to the dbus search path.
# i prefer dbus to only search XDG paths (/share/dbus-1) for service files, as that's more introspectable.
# see: <repo:nixos/nixpkgs:nixos/modules/services/system/dbus.nix>
# TODO: sandbox dbus? i pretty explicitly don't want to use it as a launcher.
services.dbus.packages = lib.mkForce [
"/run/current-system/sw"
# config.system.path
# pkgs.dbus
# pkgs.polkit.out
# pkgs.modemmanager
# pkgs.networkmanager
# pkgs.udisks
# pkgs.wpa_supplicant
];
} }

View File

@@ -4,7 +4,7 @@
{ {
sane.programs.aerc = { sane.programs.aerc = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< /share/aerc/aerc.conf refers to other /share files by absolute path sandbox.wrapperType = "inplace";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin; secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
mime.associations."x-scheme-handler/mailto" = "aerc.desktop"; mime.associations."x-scheme-handler/mailto" = "aerc.desktop";

View File

@@ -3,28 +3,14 @@
# - `man 5 alacritty` # - `man 5 alacritty`
# - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml # - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml
# - irc: #alacritty on libera.chat # - irc: #alacritty on libera.chat
{ config, lib, ... }: { lib, ... }:
let
cfg = config.sane.programs.alacritty;
in
{ {
sane.programs.alacritty = { sane.programs.alacritty = {
configOption = with lib; mkOption {
default = {};
type = types.submodule {
options.fontSize = mkOption {
type = types.int;
default = 14;
};
};
};
sandbox.enable = false; sandbox.enable = false;
env.TERMINAL = lib.mkDefault "alacritty"; env.TERMINAL = lib.mkDefault "alacritty";
fs.".config/alacritty/alacritty.toml".symlink.text = '' fs.".config/alacritty/alacritty.toml".symlink.text = ''
[font] [font]
size = ${builtins.toString cfg.config.fontSize} size = 14
[[keyboard.bindings]] [[keyboard.bindings]]
mods = "Control" mods = "Control"
@@ -50,21 +36,6 @@ in
mods = "Control|Shift" mods = "Control|Shift"
key = "PageDown" key = "PageDown"
action = "ScrollPageDown" action = "ScrollPageDown"
# disable OS shortcuts which leak through...
# see sway config or sane-input-handler for more info on why these leak through
[[keyboard.bindings]]
key = "AudioVolumeUp"
action = "None"
[[keyboard.bindings]]
key = "AudioVolumeDown"
action = "None"
[[keyboard.bindings]]
key = "Power"
action = "None"
[[keyboard.bindings]]
key = "PowerOff"
action = "None"
''; '';
}; };
} }

View File

@@ -1,65 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.alsa-ucm-conf;
in
{
sane.programs.alsa-ucm-conf = {
configOption = with lib; mkOption {
default = {};
type = types.submodule {
options.preferEarpiece = mkOption {
type = types.bool;
default = true;
};
};
};
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
# these audio files come from some revision of:
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
#
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
# e.g. `systemd.services.pulseaudio.environment`.
# that leaves more opportunity for gaps (i.e. missing a service),
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
#
# note that with these files, the following audio device support:
# - headphones work.
# - "internal earpiece" works.
# - "internal speaker" doesn't work (but that's probably because i broke the ribbon cable)
# - "analog output" doesn't work.
packageUnwrapped = pkgs.alsa-ucm-conf.overrideAttrs (upstream: {
postPatch = (upstream.postPatch or "") + ''
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
--replace 'HiFi.conf' '/Allwinner/A64/PinePhone/HiFi.conf'
substituteInPlace ucm2/Allwinner/A64/PinePhone/PinePhone.conf \
--replace 'VoiceCall.conf' '/Allwinner/A64/PinePhone/VoiceCall.conf'
'' + lib.optionalString cfg.config.preferEarpiece ''
# decrease the priority of the internal speaker so that sounds are routed
# to the earpiece by default.
# this is just personal preference.
substituteInPlace ucm2/Allwinner/A64/PinePhone/* \
--replace 'PlaybackPriority 300' 'PlaybackPriority 100'
'';
});
sandbox.enable = false; #< only provides #out/share/alsa
# alsa-lib package only looks in its $out/share/alsa to find runtime config data, by default.
# but ALSA_CONFIG_UCM2 is an env var that can override that.
# this is particularly needed by wireplumber;
# also *maybe* pipewire and pipewire-pulse.
# taken from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
env.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
enableFor.system = lib.mkIf (builtins.any (en: en) (builtins.attrValues cfg.enableFor.user)) true;
};
environment.pathsToLink = lib.mkIf cfg.enabled [
"/share/alsa/ucm2"
];
}

View File

@@ -31,6 +31,7 @@
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
persist.byStore.plaintext = [ persist.byStore.plaintext = [

View File

@@ -62,7 +62,6 @@ in
# "iw" # "iw"
"jq" "jq"
"killall" "killall"
"less"
# "libcap_ng" # for `netcap` # "libcap_ng" # for `netcap`
"lsof" "lsof"
# "miniupnpc" # "miniupnpc"
@@ -166,7 +165,6 @@ in
]; ];
consoleMediaUtils = declPackageSet [ consoleMediaUtils = declPackageSet [
"blast-ugjka" # cast audio to UPNP/DLNA devices (via pulseaudio sink)
# "catt" # cast videos to chromecast # "catt" # cast videos to chromecast
"ffmpeg" "ffmpeg"
"go2tv" # cast videos to UPNP/DLNA device (i.e. tv). "go2tv" # cast videos to UPNP/DLNA device (i.e. tv).
@@ -205,17 +203,19 @@ in
# INDIVIDUAL PACKAGE DEFINITIONS # INDIVIDUAL PACKAGE DEFINITIONS
alsaUtils.sandbox.method = "landlock"; alsaUtils.sandbox.method = "landlock";
alsaUtils.sandbox.wrapperType = "wrappedDerivation";
alsaUtils.sandbox.whitelistAudio = true; #< not strictly necessary? alsaUtils.sandbox.whitelistAudio = true; #< not strictly necessary?
backblaze-b2 = {}; backblaze-b2 = {};
blanket.sandbox.method = "bwrap"; blanket.sandbox.method = "bwrap";
blanket.sandbox.wrapperType = "wrappedDerivation";
blanket.sandbox.whitelistAudio = true; blanket.sandbox.whitelistAudio = true;
# blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested # blanket.sandbox.whitelistDbus = [ "user" ]; # TODO: untested
blanket.sandbox.whitelistWayland = true; blanket.sandbox.whitelistWayland = true;
blueberry.sandbox.method = "bwrap"; blueberry.sandbox.method = "bwrap";
blueberry.sandbox.wrapperType = "inplace"; #< various /lib scripts refer to the bins by full path blueberry.sandbox.wrapperType = "inplace"; # /etc/xdg/autostart hardcodes paths
blueberry.sandbox.whitelistWayland = true; blueberry.sandbox.whitelistWayland = true;
blueberry.sandbox.extraPaths = [ blueberry.sandbox.extraPaths = [
"/dev/rfkill" "/dev/rfkill"
@@ -225,9 +225,11 @@ in
]; ];
bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work bridge-utils.sandbox.method = "bwrap"; #< bwrap, landlock: both work
bridge-utils.sandbox.wrapperType = "wrappedDerivation";
bridge-utils.sandbox.net = "all"; bridge-utils.sandbox.net = "all";
brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive brightnessctl.sandbox.method = "landlock"; # also bwrap, but landlock is more responsive
brightnessctl.sandbox.wrapperType = "wrappedDerivation";
brightnessctl.sandbox.extraPaths = [ brightnessctl.sandbox.extraPaths = [
"/sys/class/backlight" "/sys/class/backlight"
"/sys/class/leds" "/sys/class/leds"
@@ -236,6 +238,7 @@ in
brightnessctl.sandbox.whitelistDbus = [ "system" ]; brightnessctl.sandbox.whitelistDbus = [ "system" ];
btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work btrfs-progs.sandbox.method = "bwrap"; #< bwrap, landlock: both work
btrfs-progs.sandbox.wrapperType = "wrappedDerivation";
btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs` btrfs-progs.sandbox.autodetectCliPaths = "existing"; # e.g. `btrfs filesystem df /my/fs`
"cacert.unbundled".sandbox.enable = false; "cacert.unbundled".sandbox.enable = false;
@@ -246,6 +249,7 @@ in
# cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName` # cryptsetup: typical use is `cryptsetup open /dev/loopxyz mappedName`, and creates `/dev/mapper/mappedName`
cryptsetup.sandbox.method = "landlock"; cryptsetup.sandbox.method = "landlock";
cryptsetup.sandbox.wrapperType = "wrappedDerivation";
cryptsetup.sandbox.extraPaths = [ cryptsetup.sandbox.extraPaths = [
"/dev/mapper" "/dev/mapper"
"/dev/random" "/dev/random"
@@ -259,10 +263,12 @@ in
cryptsetup.sandbox.autodetectCliPaths = "existing"; cryptsetup.sandbox.autodetectCliPaths = "existing";
ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested ddrescue.sandbox.method = "landlock"; # TODO:sandbox: untested
ddrescue.sandbox.wrapperType = "wrappedDerivation";
ddrescue.sandbox.autodetectCliPaths = "existingOrParent"; ddrescue.sandbox.autodetectCliPaths = "existingOrParent";
# auth token, preferences # auth token, preferences
delfin.sandbox.method = "bwrap"; delfin.sandbox.method = "bwrap";
delfin.sandbox.wrapperType = "wrappedDerivation";
delfin.sandbox.whitelistAudio = true; delfin.sandbox.whitelistAudio = true;
delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player delfin.sandbox.whitelistDbus = [ "user" ]; # else `mpris` plugin crashes the player
delfin.sandbox.whitelistDri = true; delfin.sandbox.whitelistDri = true;
@@ -271,6 +277,7 @@ in
delfin.persist.byStore.private = [ ".config/delfin" ]; delfin.persist.byStore.private = [ ".config/delfin" ];
dig.sandbox.method = "bwrap"; dig.sandbox.method = "bwrap";
dig.sandbox.wrapperType = "wrappedDerivation";
dig.sandbox.net = "all"; dig.sandbox.net = "all";
# creds, but also 200 MB of node modules, etc # creds, but also 200 MB of node modules, etc
@@ -286,15 +293,18 @@ in
dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested dtc.sandbox.autodetectCliPaths = true; # TODO:sandbox: untested
dtrx.sandbox.method = "bwrap"; dtrx.sandbox.method = "bwrap";
dtrx.sandbox.wrapperType = "wrappedDerivation";
dtrx.sandbox.whitelistPwd = true; dtrx.sandbox.whitelistPwd = true;
dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive dtrx.sandbox.autodetectCliPaths = "existing"; #< for the archive
duplicity = {}; duplicity = {};
e2fsprogs.sandbox.method = "landlock"; e2fsprogs.sandbox.method = "landlock";
e2fsprogs.sandbox.wrapperType = "wrappedDerivation";
e2fsprogs.sandbox.autodetectCliPaths = "existing"; e2fsprogs.sandbox.autodetectCliPaths = "existing";
efibootmgr.sandbox.method = "landlock"; efibootmgr.sandbox.method = "landlock";
efibootmgr.sandbox.wrapperType = "wrappedDerivation";
efibootmgr.sandbox.extraPaths = [ efibootmgr.sandbox.extraPaths = [
"/sys/firmware/efi" "/sys/firmware/efi"
]; ];
@@ -302,12 +312,14 @@ in
eg25-control = {}; eg25-control = {};
electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested electrum.sandbox.method = "bwrap"; # TODO:sandbox: untested
electrum.sandbox.wrapperType = "wrappedDerivation";
electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always electrum.sandbox.net = "all"; # TODO: probably want to make this run behind a VPN, always
electrum.sandbox.whitelistWayland = true; electrum.sandbox.whitelistWayland = true;
electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs! electrum.persist.byStore.cryptClearOnBoot = [ ".electrum" ]; #< TODO: use XDG dirs!
endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ]; endless-sky.persist.byStore.plaintext = [ ".local/share/endless-sky" ];
endless-sky.sandbox.method = "bwrap"; endless-sky.sandbox.method = "bwrap";
endless-sky.sandbox.wrapperType = "wrappedDerivation";
endless-sky.sandbox.whitelistAudio = true; endless-sky.sandbox.whitelistAudio = true;
endless-sky.sandbox.whitelistDri = true; endless-sky.sandbox.whitelistDri = true;
endless-sky.sandbox.whitelistWayland = true; endless-sky.sandbox.whitelistWayland = true;
@@ -318,12 +330,14 @@ in
emote.persist.byStore.plaintext = [ ".local/share/Emote" ]; emote.persist.byStore.plaintext = [ ".local/share/Emote" ];
ethtool.sandbox.method = "landlock"; ethtool.sandbox.method = "landlock";
ethtool.sandbox.wrapperType = "wrappedDerivation";
ethtool.sandbox.capabilities = [ "net_admin" ]; ethtool.sandbox.capabilities = [ "net_admin" ];
# eza `ls` replacement # eza `ls` replacement
# landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo # landlock is OK, only `whitelistPwd` doesn't make the intermediate symlinks traversable, so it breaks on e.g. ~/Videos/servo/Shows/foo
# eza.sandbox.method = "landlock"; # eza.sandbox.method = "landlock";
eza.sandbox.method = "bwrap"; eza.sandbox.method = "bwrap";
eza.sandbox.wrapperType = "wrappedDerivation"; # slow to build
eza.sandbox.autodetectCliPaths = true; eza.sandbox.autodetectCliPaths = true;
eza.sandbox.whitelistPwd = true; eza.sandbox.whitelistPwd = true;
eza.sandbox.extraHomePaths = [ eza.sandbox.extraHomePaths = [
@@ -333,9 +347,11 @@ in
]; ];
fatresize.sandbox.method = "landlock"; fatresize.sandbox.method = "landlock";
fatresize.sandbox.wrapperType = "wrappedDerivation";
fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda fatresize.sandbox.autodetectCliPaths = "parent"; # /dev/sda1 -> needs /dev/sda
fd.sandbox.method = "landlock"; fd.sandbox.method = "landlock";
fd.sandbox.wrapperType = "wrappedDerivation"; # slow to build
fd.sandbox.autodetectCliPaths = true; fd.sandbox.autodetectCliPaths = true;
fd.sandbox.whitelistPwd = true; fd.sandbox.whitelistPwd = true;
fd.sandbox.extraHomePaths = [ fd.sandbox.extraHomePaths = [
@@ -345,12 +361,15 @@ in
]; ];
ffmpeg.sandbox.method = "bwrap"; ffmpeg.sandbox.method = "bwrap";
ffmpeg.sandbox.wrapperType = "wrappedDerivation"; # slow to build
ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting ffmpeg.sandbox.autodetectCliPaths = "existingFileOrParent"; # it outputs uncreated files -> parent dir needs mounting
file.sandbox.method = "bwrap"; file.sandbox.method = "bwrap";
file.sandbox.wrapperType = "wrappedDerivation";
file.sandbox.autodetectCliPaths = true; file.sandbox.autodetectCliPaths = true;
findutils.sandbox.method = "bwrap"; findutils.sandbox.method = "bwrap";
findutils.sandbox.wrapperType = "wrappedDerivation";
findutils.sandbox.autodetectCliPaths = true; findutils.sandbox.autodetectCliPaths = true;
findutils.sandbox.whitelistPwd = true; findutils.sandbox.whitelistPwd = true;
findutils.sandbox.extraHomePaths = [ findutils.sandbox.extraHomePaths = [
@@ -362,12 +381,14 @@ in
fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ]; fluffychat-moby.persist.byStore.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
font-manager.sandbox.method = "bwrap"; font-manager.sandbox.method = "bwrap";
font-manager.packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.font-manager.override { font-manager.sandbox.wrapperType = "inplace"; # .desktop and dbus .service file refer to /libexec
font-manager.packageUnwrapped = pkgs.font-manager.override {
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0 # build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
withWebkit = false; withWebkit = false;
}); };
forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap forkstat.sandbox.method = "landlock"; #< doesn't seem to support bwrap
forkstat.sandbox.wrapperType = "wrappedDerivation";
forkstat.sandbox.extraConfig = [ forkstat.sandbox.extraConfig = [
"--sane-sandbox-keep-namespace" "pid" "--sane-sandbox-keep-namespace" "pid"
]; ];
@@ -375,7 +396,12 @@ in
"/proc" "/proc"
]; ];
# fuzzel: TODO: re-enable sandbox. i use fuzzel both as an entry system (snippets) AND an app-launcher.
# as an app-launcher, it cannot be sandboxed without over-restricting the app it launches.
# should probably make it not be an app-launcher
fuzzel.sandbox.enable = false;
fuzzel.sandbox.method = "bwrap"; #< landlock nearly works, but unable to open ~/.cache fuzzel.sandbox.method = "bwrap"; #< landlock nearly works, but unable to open ~/.cache
fuzzel.sandbox.wrapperType = "wrappedDerivation";
fuzzel.sandbox.whitelistWayland = true; fuzzel.sandbox.whitelistWayland = true;
fuzzel.persist.byStore.private = [ fuzzel.persist.byStore.private = [
# this is a file of recent selections # this is a file of recent selections
@@ -383,11 +409,12 @@ in
]; ];
gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested gawk.sandbox.method = "bwrap"; # TODO:sandbox: untested
gawk.sandbox.wrapperType = "inplace"; # /share/gawk libraries refer to /libexec gawk.sandbox.wrapperType = "inplace"; # share/gawk libraries refer to /libexec
gawk.sandbox.autodetectCliPaths = true; gawk.sandbox.autodetectCliPaths = true;
gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could. gdb.sandbox.enable = false; # gdb doesn't sandbox well. i don't know how you could.
# gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root # gdb.sandbox.method = "landlock"; # permission denied when trying to attach, even as root
gdb.sandbox.wrapperType = "wrappedDerivation";
gdb.sandbox.autodetectCliPaths = true; gdb.sandbox.autodetectCliPaths = true;
geoclue2-with-demo-agent = {}; geoclue2-with-demo-agent = {};
@@ -397,7 +424,7 @@ in
gh.persist.byStore.private = [ ".config/gh" ]; gh.persist.byStore.private = [ ".config/gh" ];
gimp.sandbox.method = "bwrap"; gimp.sandbox.method = "bwrap";
gimp.sandbox.net = "clearnet"; #< for Xwayland gimp.sandbox.wrapperType = "wrappedDerivation";
gimp.sandbox.whitelistWayland = true; gimp.sandbox.whitelistWayland = true;
gimp.sandbox.extraHomePaths = [ gimp.sandbox.extraHomePaths = [
"Pictures/albums" "Pictures/albums"
@@ -416,32 +443,39 @@ in
]; ];
"gnome.gnome-calculator".sandbox.method = "bwrap"; "gnome.gnome-calculator".sandbox.method = "bwrap";
"gnome.gnome-calculator".sandbox.wrapperType = "inplace"; # /libexec/gnome-calculator-search-provider
"gnome.gnome-calculator".sandbox.whitelistWayland = true; "gnome.gnome-calculator".sandbox.whitelistWayland = true;
# gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events. # gnome-calendar surely has data to persist, but i use it strictly to do date math, not track events.
"gnome.gnome-calendar".sandbox.method = "bwrap"; "gnome.gnome-calendar".sandbox.method = "bwrap";
"gnome.gnome-calendar".sandbox.wrapperType = "wrappedDerivation";
"gnome.gnome-calendar".sandbox.whitelistWayland = true; "gnome.gnome-calendar".sandbox.whitelistWayland = true;
"gnome.gnome-clocks".sandbox.method = "bwrap"; "gnome.gnome-clocks".sandbox.method = "bwrap";
"gnome.gnome-clocks".sandbox.wrapperType = "wrappedDerivation";
"gnome.gnome-clocks".sandbox.whitelistWayland = true; "gnome.gnome-clocks".sandbox.whitelistWayland = true;
"gnome.gnome-clocks".suggestedPrograms = [ "dconf" ]; "gnome.gnome-clocks".suggestedPrograms = [ "dconf" ];
# gnome-disks # gnome-disks
"gnome.gnome-disk-utility".sandbox.method = "bwrap"; "gnome.gnome-disk-utility".sandbox.method = "bwrap";
"gnome.gnome-disk-utility".sandbox.wrapperType = "inplace"; # /etc/xdg/autostart
"gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ]; "gnome.gnome-disk-utility".sandbox.whitelistDbus = [ "system" ];
"gnome.gnome-disk-utility".sandbox.whitelistWayland = true; "gnome.gnome-disk-utility".sandbox.whitelistWayland = true;
# seahorse: dump gnome-keyring secrets. # seahorse: dump gnome-keyring secrets.
# N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now. # N.B.: it can also manage ~/.ssh keys, but i explicitly don't add those to the sandbox for now.
"gnome.seahorse".sandbox.method = "bwrap"; "gnome.seahorse".sandbox.method = "bwrap";
"gnome.seahorse".sandbox.wrapperType = "wrappedDerivation";
"gnome.seahorse".sandbox.whitelistDbus = [ "user" ]; "gnome.seahorse".sandbox.whitelistDbus = [ "user" ];
"gnome.seahorse".sandbox.whitelistWayland = true; "gnome.seahorse".sandbox.whitelistWayland = true;
gnome-2048.sandbox.method = "bwrap"; gnome-2048.sandbox.method = "bwrap";
gnome-2048.sandbox.wrapperType = "wrappedDerivation";
gnome-2048.sandbox.whitelistWayland = true; gnome-2048.sandbox.whitelistWayland = true;
gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ]; gnome-2048.persist.byStore.plaintext = [ ".local/share/gnome-2048/scores" ];
gnome-frog.sandbox.method = "bwrap"; gnome-frog.sandbox.method = "bwrap";
gnome-frog.sandbox.wrapperType = "wrappedDerivation";
gnome-frog.sandbox.whitelistWayland = true; gnome-frog.sandbox.whitelistWayland = true;
gnome-frog.sandbox.whitelistDbus = [ "user" ]; gnome-frog.sandbox.whitelistDbus = [ "user" ];
gnome-frog.sandbox.extraPaths = [ gnome-frog.sandbox.extraPaths = [
@@ -468,9 +502,11 @@ in
# 2. no two shaded tiles can be direct N/S/E/W neighbors # 2. no two shaded tiles can be direct N/S/E/W neighbors
# - win once (1) and (2) are satisfied # - win once (1) and (2) are satisfied
"gnome.hitori".sandbox.method = "bwrap"; "gnome.hitori".sandbox.method = "bwrap";
"gnome.hitori".sandbox.wrapperType = "wrappedDerivation";
"gnome.hitori".sandbox.whitelistWayland = true; "gnome.hitori".sandbox.whitelistWayland = true;
gnugrep.sandbox.method = "bwrap"; gnugrep.sandbox.method = "bwrap";
gnugrep.sandbox.wrapperType = "wrappedDerivation";
gnugrep.sandbox.autodetectCliPaths = true; gnugrep.sandbox.autodetectCliPaths = true;
gnugrep.sandbox.whitelistPwd = true; gnugrep.sandbox.whitelistPwd = true;
gnugrep.sandbox.extraHomePaths = [ gnugrep.sandbox.extraHomePaths = [
@@ -479,24 +515,20 @@ in
".persist/plaintext" ".persist/plaintext"
]; ];
# sed: there is an edgecase of `--file=<foo>`, wherein `foo` won't be whitelisted. gnused = {};
gnused.sandbox.method = "bwrap";
gnused.sandbox.autodetectCliPaths = "existingFile";
gnused.sandbox.whitelistPwd = true; #< `-i` flag creates a temporary file in pwd (?) and then moves it.
gpsd = {}; gpsd = {};
gptfdisk.sandbox.method = "landlock"; gptfdisk.sandbox.method = "landlock";
gptfdisk.sandbox.wrapperType = "wrappedDerivation";
gptfdisk.sandbox.extraPaths = [ gptfdisk.sandbox.extraPaths = [
"/dev" "/dev"
]; ];
gptfdisk.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use gdisk on a device file. gptfdisk.sandbox.autodetectCliPaths = "existing"; #< sometimes you'll use gdisk on a device file.
grim.sandbox.method = "bwrap"; grim = {};
grim.sandbox.autodetectCliPaths = "existingOrParent";
grim.sandbox.whitelistWayland = true;
hase.sandbox.method = "bwrap"; hase.sandbox.method = "bwrap";
hase.sandbox.wrapperType = "wrappedDerivation";
hase.sandbox.net = "clearnet"; hase.sandbox.net = "clearnet";
hase.sandbox.whitelistAudio = true; hase.sandbox.whitelistAudio = true;
hase.sandbox.whitelistDri = true; hase.sandbox.whitelistDri = true;
@@ -504,12 +536,15 @@ in
# hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda` # hdparm: has to be run as sudo. e.g. `sudo hdparm -i /dev/sda`
hdparm.sandbox.method = "bwrap"; hdparm.sandbox.method = "bwrap";
hdparm.sandbox.wrapperType = "wrappedDerivation";
hdparm.sandbox.autodetectCliPaths = true; hdparm.sandbox.autodetectCliPaths = true;
host.sandbox.method = "landlock"; host.sandbox.method = "landlock";
host.sandbox.wrapperType = "wrappedDerivation";
host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server host.sandbox.net = "all"; #< technically, only needs to contact localhost's DNS server
htop.sandbox.method = "landlock"; htop.sandbox.method = "landlock";
htop.sandbox.wrapperType = "wrappedDerivation";
htop.sandbox.extraPaths = [ htop.sandbox.extraPaths = [
"/proc" "/proc"
"/sys/devices" "/sys/devices"
@@ -520,13 +555,16 @@ in
]; ];
iftop.sandbox.method = "landlock"; iftop.sandbox.method = "landlock";
iftop.sandbox.wrapperType = "wrappedDerivation";
iftop.sandbox.capabilities = [ "net_raw" ]; iftop.sandbox.capabilities = [ "net_raw" ];
# inetutils: ping, ifconfig, hostname, traceroute, whois, .... # inetutils: ping, ifconfig, hostname, traceroute, whois, ....
# N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally). # N.B.: inetutils' `ping` is shadowed by iputils' ping (by nixos, intentionally).
inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least. inetutils.sandbox.method = "landlock"; # want to keep the same netns, at least.
inetutils.sandbox.wrapperType = "wrappedDerivation";
inkscape.sandbox.method = "bwrap"; inkscape.sandbox.method = "bwrap";
inkscape.sandbox.wrapperType = "wrappedDerivation";
inkscape.sandbox.whitelistWayland = true; inkscape.sandbox.whitelistWayland = true;
inkscape.sandbox.extraHomePaths = [ inkscape.sandbox.extraHomePaths = [
"Pictures/albums" "Pictures/albums"
@@ -542,6 +580,7 @@ in
inkscape.sandbox.autodetectCliPaths = true; inkscape.sandbox.autodetectCliPaths = true;
iotop.sandbox.method = "landlock"; iotop.sandbox.method = "landlock";
iotop.sandbox.wrapperType = "wrappedDerivation";
iotop.sandbox.extraPaths = [ iotop.sandbox.extraPaths = [
"/proc" "/proc"
]; ];
@@ -549,31 +588,38 @@ in
# provides `ip`, `routel`, others # provides `ip`, `routel`, others
iproute2.sandbox.method = "landlock"; iproute2.sandbox.method = "landlock";
iproute2.sandbox.wrapperType = "wrappedDerivation";
iproute2.sandbox.net = "all"; iproute2.sandbox.net = "all";
iproute2.sandbox.capabilities = [ "net_admin" ]; iproute2.sandbox.capabilities = [ "net_admin" ];
iptables.sandbox.method = "landlock"; iptables.sandbox.method = "landlock";
iptables.sandbox.wrapperType = "wrappedDerivation";
iptables.sandbox.net = "all"; iptables.sandbox.net = "all";
iptables.sandbox.capabilities = [ "net_admin" ]; iptables.sandbox.capabilities = [ "net_admin" ];
# iputils provides `ping` (and arping, clockdiff, tracepath) # iputils provides `ping` (and arping, clockdiff, tracepath)
iputils.sandbox.method = "landlock"; iputils.sandbox.method = "landlock";
iputils.sandbox.wrapperType = "wrappedDerivation";
iputils.sandbox.net = "all"; iputils.sandbox.net = "all";
iputils.sandbox.capabilities = [ "net_raw" ]; iputils.sandbox.capabilities = [ "net_raw" ];
iw.sandbox.method = "landlock"; iw.sandbox.method = "landlock";
iw.sandbox.wrapperType = "wrappedDerivation";
iw.sandbox.net = "all"; iw.sandbox.net = "all";
iw.sandbox.capabilities = [ "net_admin" ]; iw.sandbox.capabilities = [ "net_admin" ];
jq.sandbox.method = "bwrap"; jq.sandbox.method = "bwrap";
jq.sandbox.wrapperType = "wrappedDerivation";
jq.sandbox.autodetectCliPaths = "existingFile"; jq.sandbox.autodetectCliPaths = "existingFile";
killall.sandbox.method = "landlock"; killall.sandbox.method = "landlock";
killall.sandbox.wrapperType = "wrappedDerivation";
killall.sandbox.extraPaths = [ killall.sandbox.extraPaths = [
"/proc" "/proc"
]; ];
krita.sandbox.method = "bwrap"; krita.sandbox.method = "bwrap";
krita.sandbox.wrapperType = "wrappedDerivation";
krita.sandbox.whitelistWayland = true; krita.sandbox.whitelistWayland = true;
krita.sandbox.autodetectCliPaths = "existing"; krita.sandbox.autodetectCliPaths = "existing";
krita.sandbox.extraHomePaths = [ krita.sandbox.extraHomePaths = [
@@ -591,9 +637,11 @@ in
libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only) libcap_ng.sandbox.enable = false; # there's something about /proc/$pid/fd which breaks `readlink`/stat with every sandbox technique (except capsh-only)
libnotify.sandbox.method = "bwrap"; libnotify.sandbox.method = "bwrap";
libnotify.sandbox.wrapperType = "wrappedDerivation";
libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send libnotify.sandbox.whitelistDbus = [ "user" ]; # notify-send
losslesscut-bin.sandbox.method = "bwrap"; losslesscut-bin.sandbox.method = "bwrap";
losslesscut-bin.sandbox.wrapperType = "wrappedDerivation";
losslesscut-bin.sandbox.extraHomePaths = [ losslesscut-bin.sandbox.extraHomePaths = [
"Music" "Music"
"Pictures/from" # videos from e.g. mobile phone "Pictures/from" # videos from e.g. mobile phone
@@ -608,11 +656,12 @@ in
losslesscut-bin.sandbox.whitelistX = true; losslesscut-bin.sandbox.whitelistX = true;
lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to / lsof.sandbox.method = "capshonly"; # lsof doesn't sandbox under bwrap or even landlock w/ full access to /
lsof.sandbox.wrapperType = "wrappedDerivation";
lua = {}; lua = {};
"mate.engrampa".packageUnwrapped = pkgs.rmDbusServices pkgs.mate.engrampa;
"mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested "mate.engrampa".sandbox.method = "bwrap"; # TODO:sandbox: untested
"mate.engrampa".sandbox.wrapperType = "inplace";
"mate.engrampa".sandbox.whitelistWayland = true; "mate.engrampa".sandbox.whitelistWayland = true;
"mate.engrampa".sandbox.autodetectCliPaths = "existingOrParent"; "mate.engrampa".sandbox.autodetectCliPaths = "existingOrParent";
"mate.engrampa".sandbox.extraHomePaths = [ "mate.engrampa".sandbox.extraHomePaths = [
@@ -625,6 +674,7 @@ in
]; ];
mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested mercurial.sandbox.method = "bwrap"; # TODO:sandbox: untested
mercurial.sandbox.wrapperType = "wrappedDerivation";
mercurial.sandbox.net = "clearnet"; mercurial.sandbox.net = "clearnet";
mercurial.sandbox.whitelistPwd = true; mercurial.sandbox.whitelistPwd = true;
@@ -632,6 +682,7 @@ in
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured? # XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
monero-gui.persist.byStore.plaintext = [ ".bitmonero" ]; monero-gui.persist.byStore.plaintext = [ ".bitmonero" ];
monero-gui.sandbox.method = "bwrap"; monero-gui.sandbox.method = "bwrap";
monero-gui.sandbox.wrapperType = "wrappedDerivation";
monero-gui.sandbox.net = "all"; monero-gui.sandbox.net = "all";
monero-gui.sandbox.extraHomePaths = [ monero-gui.sandbox.extraHomePaths = [
"records/finance/cryptocurrencies/monero" "records/finance/cryptocurrencies/monero"
@@ -640,16 +691,20 @@ in
mumble.persist.byStore.private = [ ".local/share/Mumble" ]; mumble.persist.byStore.private = [ ".local/share/Mumble" ];
nano.sandbox.method = "bwrap"; nano.sandbox.method = "bwrap";
nano.sandbox.wrapperType = "wrappedDerivation";
nano.sandbox.autodetectCliPaths = "existingFileOrParent"; nano.sandbox.autodetectCliPaths = "existingFileOrParent";
netcat.sandbox.method = "landlock"; netcat.sandbox.method = "landlock";
netcat.sandbox.wrapperType = "wrappedDerivation";
netcat.sandbox.net = "all"; netcat.sandbox.net = "all";
nethogs.sandbox.method = "capshonly"; # *partially* works under landlock w/ full access to / nethogs.sandbox.method = "capshonly"; # *partially* works under landlock w/ full access to /
nethogs.sandbox.wrapperType = "wrappedDerivation";
nethogs.sandbox.capabilities = [ "net_admin" "net_raw" ]; nethogs.sandbox.capabilities = [ "net_admin" "net_raw" ];
# provides `arp`, `hostname`, `route`, `ifconfig` # provides `arp`, `hostname`, `route`, `ifconfig`
nettools.sandbox.method = "landlock"; nettools.sandbox.method = "landlock";
nettools.sandbox.wrapperType = "wrappedDerivation";
nettools.sandbox.net = "all"; nettools.sandbox.net = "all";
nettools.sandbox.capabilities = [ "net_admin" "net_raw" ]; nettools.sandbox.capabilities = [ "net_admin" "net_raw" ];
nettools.sandbox.extraPaths = [ nettools.sandbox.extraPaths = [
@@ -657,6 +712,7 @@ in
]; ];
networkmanagerapplet.sandbox.method = "bwrap"; networkmanagerapplet.sandbox.method = "bwrap";
networkmanagerapplet.sandbox.wrapperType = "wrappedDerivation";
networkmanagerapplet.sandbox.whitelistWayland = true; networkmanagerapplet.sandbox.whitelistWayland = true;
networkmanagerapplet.sandbox.whitelistDbus = [ "system" ]; networkmanagerapplet.sandbox.whitelistDbus = [ "system" ];
@@ -669,9 +725,11 @@ in
]; ];
nmap.sandbox.method = "bwrap"; nmap.sandbox.method = "bwrap";
nmap.sandbox.wrapperType = "wrappedDerivation";
nmap.sandbox.net = "all"; # clearnet and lan nmap.sandbox.net = "all"; # clearnet and lan
nmon.sandbox.method = "landlock"; nmon.sandbox.method = "landlock";
nmon.sandbox.wrapperType = "wrappedDerivation";
nmon.sandbox.extraPaths = [ nmon.sandbox.extraPaths = [
"/proc" "/proc"
]; ];
@@ -680,6 +738,7 @@ in
# `nvme list` only shows results when run as root. # `nvme list` only shows results when run as root.
nvme-cli.sandbox.method = "landlock"; nvme-cli.sandbox.method = "landlock";
nvme-cli.sandbox.wrapperType = "wrappedDerivation";
nvme-cli.sandbox.extraPaths = [ nvme-cli.sandbox.extraPaths = [
"/sys/devices" "/sys/devices"
"/sys/class/nvme" "/sys/class/nvme"
@@ -691,11 +750,13 @@ in
# contains only `oathtool`, which i only use for evaluating TOTP codes from CLI/stdin # contains only `oathtool`, which i only use for evaluating TOTP codes from CLI/stdin
oath-toolkit.sandbox.method = "bwrap"; oath-toolkit.sandbox.method = "bwrap";
oath-toolkit.sandbox.wrapperType = "wrappedDerivation";
# settings (electron app) # settings (electron app)
obsidian.persist.byStore.plaintext = [ ".config/obsidian" ]; obsidian.persist.byStore.plaintext = [ ".config/obsidian" ];
parted.sandbox.method = "landlock"; parted.sandbox.method = "landlock";
parted.sandbox.wrapperType = "wrappedDerivation";
parted.sandbox.extraPaths = [ parted.sandbox.extraPaths = [
"/dev" "/dev"
]; ];
@@ -704,10 +765,12 @@ in
patchelf = {}; patchelf = {};
pavucontrol.sandbox.method = "bwrap"; pavucontrol.sandbox.method = "bwrap";
pavucontrol.sandbox.wrapperType = "wrappedDerivation";
pavucontrol.sandbox.whitelistAudio = true; pavucontrol.sandbox.whitelistAudio = true;
pavucontrol.sandbox.whitelistWayland = true; pavucontrol.sandbox.whitelistWayland = true;
pciutils.sandbox.method = "landlock"; pciutils.sandbox.method = "landlock";
pciutils.sandbox.wrapperType = "wrappedDerivation";
pciutils.sandbox.extraPaths = [ pciutils.sandbox.extraPaths = [
"/sys/bus/pci" "/sys/bus/pci"
"/sys/devices" "/sys/devices"
@@ -716,6 +779,7 @@ in
"perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`. "perlPackages.FileMimeInfo".sandbox.enable = false; #< TODO: sandbox `mimetype` but not `mimeopen`.
powertop.sandbox.method = "landlock"; powertop.sandbox.method = "landlock";
powertop.sandbox.wrapperType = "wrappedDerivation";
powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ]; powertop.sandbox.capabilities = [ "ipc_lock" "sys_admin" ];
powertop.sandbox.extraPaths = [ powertop.sandbox.extraPaths = [
"/proc" "/proc"
@@ -724,23 +788,18 @@ in
"/sys/kernel" "/sys/kernel"
]; ];
# procps: free, pgrep, pidof, pkill, ps, pwait, top, uptime, couple others
procps.sandbox.method = "bwrap";
procps.sandbox.extraConfig = [
"--sane-sandbox-keep-namespace" "pid"
];
pstree.sandbox.method = "landlock"; pstree.sandbox.method = "landlock";
pstree.sandbox.wrapperType = "wrappedDerivation";
pstree.sandbox.extraPaths = [ pstree.sandbox.extraPaths = [
"/proc" "/proc"
]; ];
pulseaudio = {};
pulsemixer.sandbox.method = "landlock"; pulsemixer.sandbox.method = "landlock";
pulsemixer.sandbox.wrapperType = "wrappedDerivation";
pulsemixer.sandbox.whitelistAudio = true; pulsemixer.sandbox.whitelistAudio = true;
pwvucontrol.sandbox.method = "bwrap"; pwvucontrol.sandbox.method = "bwrap";
pwvucontrol.sandbox.wrapperType = "wrappedDerivation";
pwvucontrol.sandbox.whitelistAudio = true; pwvucontrol.sandbox.whitelistAudio = true;
pwvucontrol.sandbox.whitelistWayland = true; pwvucontrol.sandbox.whitelistWayland = true;
@@ -748,6 +807,7 @@ in
requests requests
]); ]);
python3-repl.sandbox.method = "bwrap"; python3-repl.sandbox.method = "bwrap";
python3-repl.sandbox.wrapperType = "wrappedDerivation";
python3-repl.sandbox.net = "clearnet"; python3-repl.sandbox.net = "clearnet";
python3-repl.sandbox.extraHomePaths = [ python3-repl.sandbox.extraHomePaths = [
"/" "/"
@@ -758,24 +818,22 @@ in
qemu.slowToBuild = true; qemu.slowToBuild = true;
rsync.sandbox.method = "bwrap"; rsync.sandbox.method = "bwrap";
rsync.sandbox.wrapperType = "wrappedDerivation";
rsync.sandbox.net = "clearnet"; rsync.sandbox.net = "clearnet";
rsync.sandbox.autodetectCliPaths = "existingOrParent"; rsync.sandbox.autodetectCliPaths = "existingOrParent";
rustc = {}; rustc = {};
sane-open-desktop.sandbox.enable = false; #< trivial script, and all our deps are sandboxed
sane-open-desktop.suggestedPrograms = [
"gdbus"
];
screen.sandbox.enable = false; #< tty; needs to run anything screen.sandbox.enable = false; #< tty; needs to run anything
sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested sequoia.sandbox.method = "bwrap"; # TODO:sandbox: untested
sequoia.sandbox.wrapperType = "wrappedDerivation"; # slow to build
sequoia.sandbox.whitelistPwd = true; sequoia.sandbox.whitelistPwd = true;
sequoia.sandbox.autodetectCliPaths = true; sequoia.sandbox.autodetectCliPaths = true;
shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ]; shattered-pixel-dungeon.persist.byStore.plaintext = [ ".local/share/.shatteredpixel/shattered-pixel-dungeon" ];
shattered-pixel-dungeon.sandbox.method = "bwrap"; shattered-pixel-dungeon.sandbox.method = "bwrap";
shattered-pixel-dungeon.sandbox.wrapperType = "wrappedDerivation";
shattered-pixel-dungeon.sandbox.whitelistAudio = true; shattered-pixel-dungeon.sandbox.whitelistAudio = true;
shattered-pixel-dungeon.sandbox.whitelistDri = true; shattered-pixel-dungeon.sandbox.whitelistDri = true;
shattered-pixel-dungeon.sandbox.whitelistWayland = true; shattered-pixel-dungeon.sandbox.whitelistWayland = true;
@@ -783,8 +841,7 @@ in
# printer/filament settings # printer/filament settings
slic3r.persist.byStore.plaintext = [ ".Slic3r" ]; slic3r.persist.byStore.plaintext = [ ".Slic3r" ];
slurp.sandbox.method = "bwrap"; slurp = {};
slurp.sandbox.whitelistWayland = true;
# use like `sudo smartctl /dev/sda -a` # use like `sudo smartctl /dev/sda -a`
smartmontools.sandbox.method = "landlock"; smartmontools.sandbox.method = "landlock";
@@ -793,6 +850,7 @@ in
smartmontools.sandbox.capabilities = [ "sys_rawio" ]; smartmontools.sandbox.capabilities = [ "sys_rawio" ];
sops.sandbox.method = "bwrap"; # TODO:sandbox: untested sops.sandbox.method = "bwrap"; # TODO:sandbox: untested
sops.sandbox.wrapperType = "wrappedDerivation";
sops.sandbox.extraHomePaths = [ sops.sandbox.extraHomePaths = [
".config/sops" ".config/sops"
"dev/nixos" "dev/nixos"
@@ -802,6 +860,7 @@ in
]; ];
soundconverter.sandbox.method = "bwrap"; soundconverter.sandbox.method = "bwrap";
soundconverter.sandbox.wrapperType = "wrappedDerivation";
soundconverter.sandbox.whitelistWayland = true; soundconverter.sandbox.whitelistWayland = true;
soundconverter.sandbox.extraHomePaths = [ soundconverter.sandbox.extraHomePaths = [
"Music" "Music"
@@ -815,16 +874,19 @@ in
soundconverter.sandbox.autodetectCliPaths = "existingOrParent"; soundconverter.sandbox.autodetectCliPaths = "existingOrParent";
sox.sandbox.method = "bwrap"; sox.sandbox.method = "bwrap";
sox.sandbox.wrapperType = "wrappedDerivation";
sox.sandbox.autodetectCliPaths = "existingFileOrParent"; sox.sandbox.autodetectCliPaths = "existingFileOrParent";
sox.sandbox.whitelistAudio = true; sox.sandbox.whitelistAudio = true;
space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ]; space-cadet-pinball.persist.byStore.plaintext = [ ".local/share/SpaceCadetPinball" ];
space-cadet-pinball.sandbox.method = "bwrap"; space-cadet-pinball.sandbox.method = "bwrap";
space-cadet-pinball.sandbox.wrapperType = "wrappedDerivation";
space-cadet-pinball.sandbox.whitelistAudio = true; space-cadet-pinball.sandbox.whitelistAudio = true;
space-cadet-pinball.sandbox.whitelistDri = true; space-cadet-pinball.sandbox.whitelistDri = true;
space-cadet-pinball.sandbox.whitelistWayland = true; space-cadet-pinball.sandbox.whitelistWayland = true;
speedtest-cli.sandbox.method = "bwrap"; speedtest-cli.sandbox.method = "bwrap";
speedtest-cli.sandbox.wrapperType = "wrappedDerivation";
speedtest-cli.sandbox.net = "all"; speedtest-cli.sandbox.net = "all";
sqlite = {}; sqlite = {};
@@ -832,6 +894,7 @@ in
strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything* strace.sandbox.enable = false; #< needs to `exec` its args, and therefore support *anything*
subversion.sandbox.method = "bwrap"; subversion.sandbox.method = "bwrap";
subversion.sandbox.wrapperType = "wrappedDerivation";
subversion.sandbox.net = "clearnet"; subversion.sandbox.net = "clearnet";
subversion.sandbox.whitelistPwd = true; subversion.sandbox.whitelistPwd = true;
sudo.sandbox.enable = false; sudo.sandbox.enable = false;
@@ -843,11 +906,8 @@ in
superTux.sandbox.whitelistWayland = true; superTux.sandbox.whitelistWayland = true;
superTux.persist.byStore.plaintext = [ ".local/share/supertux2" ]; superTux.persist.byStore.plaintext = [ ".local/share/supertux2" ];
swappy.sandbox.method = "bwrap";
swappy.sandbox.autodetectCliPaths = "existingFileOrParent";
swappy.sandbox.whitelistWayland = true;
tcpdump.sandbox.method = "landlock"; tcpdump.sandbox.method = "landlock";
tcpdump.sandbox.wrapperType = "wrappedDerivation";
tcpdump.sandbox.net = "all"; tcpdump.sandbox.net = "all";
tcpdump.sandbox.autodetectCliPaths = "existingFileOrParent"; tcpdump.sandbox.autodetectCliPaths = "existingFileOrParent";
tcpdump.sandbox.capabilities = [ "net_admin" "net_raw" ]; tcpdump.sandbox.capabilities = [ "net_admin" "net_raw" ];
@@ -857,10 +917,12 @@ in
tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ]; tokodon.persist.byStore.private = [ ".cache/KDE/tokodon" ];
tree.sandbox.method = "landlock"; tree.sandbox.method = "landlock";
tree.sandbox.wrapperType = "wrappedDerivation";
tree.sandbox.autodetectCliPaths = true; tree.sandbox.autodetectCliPaths = true;
tree.sandbox.whitelistPwd = true; tree.sandbox.whitelistPwd = true;
tumiki-fighters.sandbox.method = "bwrap"; tumiki-fighters.sandbox.method = "bwrap";
tumiki-fighters.sandbox.wrapperType = "wrappedDerivation";
tumiki-fighters.sandbox.whitelistAudio = true; tumiki-fighters.sandbox.whitelistAudio = true;
tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf tumiki-fighters.sandbox.whitelistDri = true; #< not strictly necessary, but triples CPU perf
tumiki-fighters.sandbox.whitelistWayland = true; tumiki-fighters.sandbox.whitelistWayland = true;
@@ -869,28 +931,34 @@ in
util-linux.sandbox.enable = false; #< TODO: possible to sandbox if i specific a different profile for each of its ~50 binaries util-linux.sandbox.enable = false; #< TODO: possible to sandbox if i specific a different profile for each of its ~50 binaries
unzip.sandbox.method = "bwrap"; unzip.sandbox.method = "bwrap";
unzip.sandbox.wrapperType = "wrappedDerivation";
unzip.sandbox.autodetectCliPaths = "existingOrParent"; unzip.sandbox.autodetectCliPaths = "existingOrParent";
unzip.sandbox.whitelistPwd = true; unzip.sandbox.whitelistPwd = true;
usbutils.sandbox.method = "bwrap"; # breaks `usbhid-dump`, but `lsusb`, `usb-devices` work usbutils.sandbox.method = "bwrap"; # breaks `usbhid-dump`, but `lsusb`, `usb-devices` work
usbutils.sandbox.wrapperType = "wrappedDerivation";
usbutils.sandbox.extraPaths = [ usbutils.sandbox.extraPaths = [
"/sys/devices" "/sys/devices"
"/sys/bus/usb" "/sys/bus/usb"
]; ];
visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested visidata.sandbox.method = "bwrap"; # TODO:sandbox: untested
visidata.sandbox.wrapperType = "wrappedDerivation";
visidata.sandbox.autodetectCliPaths = true; visidata.sandbox.autodetectCliPaths = true;
# `vulkaninfo`, `vkcube` # `vulkaninfo`, `vkcube`
vulkan-tools.sandbox.method = "landlock"; vulkan-tools.sandbox.method = "landlock";
vulkan-tools.sandbox.wrapperType = "wrappedDerivation";
vvvvvv.sandbox.method = "bwrap"; vvvvvv.sandbox.method = "bwrap";
vvvvvv.sandbox.wrapperType = "wrappedDerivation";
vvvvvv.sandbox.whitelistAudio = true; vvvvvv.sandbox.whitelistAudio = true;
vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU vvvvvv.sandbox.whitelistDri = true; #< playable without, but burns noticably more CPU
vvvvvv.sandbox.whitelistWayland = true; vvvvvv.sandbox.whitelistWayland = true;
vvvvvv.persist.byStore.plaintext = [ ".local/share/VVVVVV" ]; vvvvvv.persist.byStore.plaintext = [ ".local/share/VVVVVV" ];
w3m.sandbox.method = "bwrap"; w3m.sandbox.method = "bwrap";
w3m.sandbox.wrapperType = "wrappedDerivation";
w3m.sandbox.net = "all"; w3m.sandbox.net = "all";
w3m.sandbox.extraHomePaths = [ w3m.sandbox.extraHomePaths = [
# little-used feature, but you can save web pages :) # little-used feature, but you can save web pages :)
@@ -898,9 +966,11 @@ in
]; ];
wdisplays.sandbox.method = "bwrap"; wdisplays.sandbox.method = "bwrap";
wdisplays.sandbox.wrapperType = "wrappedDerivation";
wdisplays.sandbox.whitelistWayland = true; wdisplays.sandbox.whitelistWayland = true;
wget.sandbox.method = "bwrap"; wget.sandbox.method = "bwrap";
wget.sandbox.wrapperType = "wrappedDerivation";
wget.sandbox.net = "all"; wget.sandbox.net = "all";
wget.sandbox.whitelistPwd = true; # saves to pwd by default wget.sandbox.whitelistPwd = true; # saves to pwd by default
@@ -908,13 +978,16 @@ in
# `wg`, `wg-quick` # `wg`, `wg-quick`
wireguard-tools.sandbox.method = "landlock"; wireguard-tools.sandbox.method = "landlock";
wireguard-tools.sandbox.wrapperType = "wrappedDerivation";
wireguard-tools.sandbox.capabilities = [ "net_admin" ]; wireguard-tools.sandbox.capabilities = [ "net_admin" ];
# provides `iwconfig`, `iwlist`, `iwpriv`, ... # provides `iwconfig`, `iwlist`, `iwpriv`, ...
wirelesstools.sandbox.method = "landlock"; wirelesstools.sandbox.method = "landlock";
wirelesstools.sandbox.wrapperType = "wrappedDerivation";
wirelesstools.sandbox.capabilities = [ "net_admin" ]; wirelesstools.sandbox.capabilities = [ "net_admin" ];
wl-clipboard.sandbox.method = "bwrap"; wl-clipboard.sandbox.method = "bwrap";
wl-clipboard.sandbox.wrapperType = "wrappedDerivation";
wl-clipboard.sandbox.whitelistWayland = true; wl-clipboard.sandbox.whitelistWayland = true;
wtype = {}; wtype = {};
@@ -925,11 +998,13 @@ in
xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic) xwayland.sandbox.net = "clearnet"; #< just assuming this is needed (X11 traffic)
xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf xwayland.sandbox.whitelistDri = true; #< would assume this gives better gfx perf
xdg-terminal-exec.sandbox.enable = false; # xdg-terminal-exec is a launcher for $TERM
xterm.sandbox.enable = false; # need to be able to do everything xterm.sandbox.enable = false; # need to be able to do everything
yarn.persist.byStore.plaintext = [ ".cache/yarn" ]; yarn.persist.byStore.plaintext = [ ".cache/yarn" ];
yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested yt-dlp.sandbox.method = "bwrap"; # TODO:sandbox: untested
yt-dlp.sandbox.wrapperType = "wrappedDerivation";
yt-dlp.sandbox.net = "all"; yt-dlp.sandbox.net = "all";
yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default yt-dlp.sandbox.whitelistPwd = true; # saves to pwd by default

View File

@@ -10,6 +10,7 @@
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.autodetectCliPaths = true; sandbox.autodetectCliPaths = true;

View File

@@ -88,6 +88,7 @@ in
{ {
sane.programs.bemenu = { sane.programs.bemenu = {
sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted. sandbox.method = "bwrap"; # landlock works, but requires *all* of /run/user/$ID to be granted.
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
".cache/fontconfig" #< else it complains, and is *way* slower ".cache/fontconfig" #< else it complains, and is *way* slower

View File

@@ -1,216 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p blast-ugjka
# vim: set filetype=python :
import ctypes
import logging
import os
import signal
import socket
import subprocess
from enum import Enum
logger = logging.getLogger(__name__)
# map from known devices -> required flags
DEVICE_MAP = {
"Theater TV": [],
"[LG] webOS TV OLED55C9PUA": [ "-usewav" ],
}
def set_pdeathsig(sig=signal.SIGTERM):
"""
helper function to ensure once parent process exits, its children processes will automatically die.
see: <https://stackoverflow.com/a/43152455>
see: <https://www.man7.org/linux/man-pages/man2/prctl.2.html>
"""
libc = ctypes.CDLL("libc.so.6")
return libc.prctl(1, sig)
MY_PID = None
def reap_children(sig=None, frame=None):
global MY_PID
# reset SIGTERM handler to avoid recursing
signal.signal(signal.SIGTERM, signal.Handlers.SIG_DFL)
logger.info("killing all children (of pid %d)", MY_PID)
os.killpg(MY_PID, signal.SIGTERM)
def reap_on_exit():
"""
catch when the parent exits, and map that to SIGTERM for this process.
when this process receives SIGTERM, also terminate all descendent processes.
this is done because:
1. mpv invokes this, but (potentially) via the sandbox wrapper.
2. when mpv exits, it `SIGKILL`s that sandbox wrapper.
3. bwrap does not pass SIGKILL or SIGTERM to its child.
4. hence, we neither receive that signal NOR can we pass it on simply by killing our immediate children
(since any bwrap'd children wouldn't pass that signal on...)
really, the proper fix would be on mpv's side:
- mpv should create a new process group when it launches a command, and kill that process group on exit.
or fix this in the sandbox wrapper:
- why *doesn't* bwrap forward the signals?
- there's --die-with-parent, but i can't apply that *system wide* and expect reasonably behavior
<https://github.com/containers/bubblewrap/issues/529>
"""
global MY_PID
MY_PID = os.getpid()
# create a new process group, pgid = gid
os.setpgid(MY_PID, MY_PID)
set_pdeathsig(signal.SIGTERM)
signal.signal(signal.SIGTERM, reap_children)
def get_ranked_ip_addrs():
"""
return the IP addresses most likely to be LAN addresses
based on: <https://stackoverflow.com/a/1267524>
"""
_name, _aliases, static_addrs = socket.gethostbyname_ex(socket.gethostname())
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("1", 53))
con_addr, _port = s.getsockname()
return sorted(set(static_addrs + [ con_addr ]), key=lambda a: (a.startswith("127"), a))
class ParserState(Enum):
Break = "break"
Receiver = "receiver"
Ips = "ip"
class Status(Enum):
Continue = "continue"
Error = "error"
RedoWithFlags = "redo_with_flags"
Launched = "launched"
class BlastDriver:
parsing: ParserState | None = None
last_write: str | None = None
def __init__(self, blast_flags: list[str] = []):
self.ranked_ips = get_ranked_ip_addrs()
self.blast = subprocess.Popen(
["blast", "-source", "blast.monitor"] + blast_flags,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
# this pdeathsig isn't necessary; seems it might result in leaked pulse outputs
# preexec_fn=set_pdeathsig
)
self.blast_flags = list(blast_flags)
self.receiver_names = []
self.ips = []
def writeline(self, line: str) -> None:
logger.debug("[send] %s", line)
self.blast.stdin.write(f"{line}\n".encode())
self.blast.stdin.flush()
self.last_write = line
def readline(self) -> str:
line = self.blast.stdout.readline().decode('utf-8').strip()
line = line.replace('\x1b[1A\x1b[K', '') #< escape codes
logger.debug("[recv] %r", line)
return line
def set_state(self, state: ParserState):
logger.debug("[pars] %s", state)
self.parsing = state
def feedline(self, line: str) -> (Status, str|None):
"""
apply a line from blast's stdout to modify parser state.
returns a status code (e.g. Status.Continue), and optionally a reply to send back to blast.
"""
if line == "Loading...":
return Status.Continue, None
elif line == "----------":
self.set_state(ParserState.Break)
return Status.Continue, None
elif line == "DLNA receivers":
self.set_state(ParserState.Receiver)
return Status.Continue, None
elif line == "Your LAN ip addresses":
self.set_state(ParserState.Ips)
return Status.Continue, None
elif line == "Select the DLNA device:":
assert len(self.receiver_names) == 1, self.receiver_names
name = self.receiver_names[0]
if name in DEVICE_MAP and DEVICE_MAP[name] != self.blast_flags:
return Status.RedoWithFlags, None
return Status.Continue, "0"
elif line == "Select the lan IP address for the stream:":
for r in self.ranked_ips:
if r in self.ips:
return Status.Launched, str(self.ips.index(r))
# fallback: just guess the best IP
return Status.Launched, "0"
elif self.parsing == ParserState.Receiver:
id_, name = line.split(": ")
assert id_ == str(len(self.receiver_names)), (id_, self.receiver_names)
self.receiver_names.append(name)
return Status.Continue, None
elif self.parsing == ParserState.Ips:
id_, ip = line.split(": ")
assert id_ == str(len(self.ips)), (id_, self.ips)
self.ips.append(ip)
return Status.Continue, None
elif line == f"[{self.last_write}]":
# it's echoing to us what we wrote
return Status.Continue, None
# elif line == "":
# return Status.Continue, None
else:
logger.info("unrecognized output (state=%s): %r", self.parsing, line)
return Status.Error, None
def step(self) -> Status:
"""
advance the interaction between us and blast.
reads a line from blast, modifies internal state, maybe sends a reply.
could block indefinitely.
"""
line = self.readline()
status, reply = self.feedline(line)
if reply is not None:
self.writeline(reply)
return status
def try_blast(*args, **kwargs) -> BlastDriver | None:
blast = BlastDriver(*args, **kwargs)
status = Status.Continue
while status == Status.Continue:
status = blast.step()
if status == Status.RedoWithFlags:
dev = blast.receiver_names[0]
blast_flags = DEVICE_MAP[dev]
logger.info("re-exec blast for %s with flags: %r", dev, blast_flags)
blast.blast.terminate()
return try_blast(blast_flags=blast_flags)
elif status == Status.Error:
logger.info("blast error => terminating")
blast.blast.terminate()
else:
# successfully launched
return blast
def main():
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
reap_on_exit()
blast = try_blast()
if blast is not None:
logger.info("waiting until blast exits")
blast.blast.wait()
reap_children()
if __name__ == "__main__":
main()

View File

@@ -1,51 +0,0 @@
# blast: tunnel audio from a pulseaudio sink to a UPnP/DLNA device (like a TV).
# - expect 7s of latency
# - can cast the default sink, or create a new one "blast.monitor"
# and either assign that to default or assign apps to it.
# compatibility:
# - there is no single invocation which will be compatible with all known devices.
# - sony tv:
# - `blast` (default): WORKS
# - `-usewav`: FAILS
# - LG TV:
# - `-usewav`: WORKS!
# - `-useaac`: FAILS
# - `-useflac`: FAILS
# - `-uselpcm`: FAILS
# - `-uselpcmle`: FAILS
# - `-format aac`: FAILS
# - `-bitrate 128`: FAILS
# - `-nochunked`: FAILS
# - `-format "ogg" -mime 'audio/x-opus+ogg'`: FAILS
# - `-mime audio/ac3 -format ac3`: FAILS
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.blast-ugjka;
in
{
sane.programs.blast-ugjka = {
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.net = "clearnet";
};
sane.programs.blast-to-default = {
# helper to deal with blast's interactive CLI
packageUnwrapped = pkgs.static-nix-shell.mkPython3Bin {
pname = "blast-to-default";
pkgs = [ "blast-ugjka" ];
srcRoot = ./.;
};
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.net = "clearnet";
sandbox.extraConfig = [
# else it fails to reap its children (or, maybe, it fails to hook its parent's death signal?)
# might be possible to remove this, but kinda hard to see a clean way.
"--sane-sandbox-keep-namespace" "pid"
];
suggestedPrograms = [ "blast-ugjka" ];
};
networking.firewall.allowedTCPPorts = lib.mkIf cfg.enabled [ 9000 ];
}

View File

@@ -111,11 +111,6 @@ in
}; };
}; };
sandbox.method = "bwrap";
sandbox.extraRuntimePaths = [
"/" #< just needs "bonsai", but needs to create it first...
];
services.bonsaid = { services.bonsaid = {
description = "bonsai: programmable input dispatcher"; description = "bonsai: programmable input dispatcher";
after = [ "graphical-session.target" ]; after = [ "graphical-session.target" ];

View File

@@ -1,17 +0,0 @@
{ ... }:
{
sane.programs.celeste64 = {
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDri = true;
sandbox.whitelistWayland = true;
sandbox.extraPaths = [
"/dev/input" #< for controllers
];
persist.byStore.plaintext = [
# save data, controls map
".local/share/Celeste64"
];
};
}

View File

@@ -1,19 +1,9 @@
{ pkgs, ... }: { ... }:
{ {
sane.programs.cozy = { sane.programs.cozy = {
packageUnwrapped = pkgs.cozy.overrideAttrs (upstream: {
postPatch = (upstream.postPatch or "") + ''
# disable all reporting.
# this can be done via the settings, but that's troublesome and easy to forget.
# specifically, i don't want moby to be making these network requests several times per hour
# while it might be roaming or trying to put the RF to sleep.
substituteInPlace cozy/application_settings.py \
--replace-fail 'self._settings.get_int("report-level")' '0'
'';
});
sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied sandbox.method = "bwrap"; # landlock gives: _multiprocessing.SemLock: Permission Denied
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # mpris sandbox.whitelistDbus = [ "user" ]; # mpris
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -9,47 +9,17 @@ let
in in
{ {
sane.programs.dconf = { sane.programs.dconf = {
configOption = with lib; mkOption {
type = types.submodule {
options = {
site = mkOption {
type = types.listOf types.package;
default = [];
description = ''
extra packages to link into /etc/dconf
'';
};
};
};
default = {};
};
packageUnwrapped = pkgs.rmDbusServicesInPlace pkgs.dconf;
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< dbus/systemd services live in `.out` but point to `.lib` data. sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ];
persist.byStore.private = [ persist.byStore.private = [
".config/dconf" ".config/dconf"
]; ];
};
services.dconf = { programs.dconf = lib.mkIf cfg.enabled {
description = "dconf configuration database/server"; # note that `programs.dconf` doesn't allow specifying the dconf package.
after = [ "graphical-session.target" ]; enable = true;
wantedBy = [ "graphical-session.target" ]; packages = [
serviceConfig = {
ExecStart = "${lib.getLib cfg.package}/libexec/dconf-service";
Type = "simple";
Restart = "always";
RestartSec = "5s";
};
};
# supposedly necessary for packages which haven't been wrapped (i.e. wrapGtkApp?),
# but in practice seems unnecessary.
# env.GIO_EXTRA_MODULES = "${pkgs.dconf.lib}/lib/gio/modules";
config.site = [
(pkgs.writeTextFile { (pkgs.writeTextFile {
name = "dconf-user-profile"; name = "dconf-user-profile";
destination = "/etc/dconf/profile/user"; destination = "/etc/dconf/profile/user";
@@ -60,18 +30,4 @@ in
}) })
]; ];
}; };
# TODO: get dconf to read these from ~/.config/dconf ?
environment.etc.dconf = lib.mkIf cfg.enabled {
source = pkgs.symlinkJoin {
name = "dconf-system-config";
paths = map (x: "${x}/etc/dconf") cfg.config.site;
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
postBuild = ''
if test -d $out/db; then
dconf update $out/db
fi
'';
};
};
} }

View File

@@ -1,139 +0,0 @@
/* Notification center */
.blurredBG, #main_window, .blurredBG.low, .blurredBG.normal {
background: rgba(255, 255, 255, 1.0);
}
.noti-center.time {
font-size: 32px;
}
/* Notifications */
.notification.content {
margin-left: 15px;
margin-right: 15px;
}
.title {
font-weight: bold;
font-size: 16px;
}
.appname {
font-size: 12px;
}
.time {
font-size: 12px;
}
.blurredBG.notification {
background: rgba(255, 255, 255, 0.4);
}
.blurredBG.notification.critical {
background: rgba(255, 0, 0, 0.5);
}
.notificationInCenter.critical {
background: rgba(155, 0, 20, 0.5);
}
/* Labels */
label {
color: #322;
}
label.notification {
color: #322;
}
label.critical {
color: #000;
}
.notificationInCenter label.critical {
color: #000;
}
/* Buttons */
button {
background: transparent;
color: #322;
border-radius: 3px;
border-width: 0px;
background-position: 0px 0px;
text-shadow: none;
}
button:hover {
border-radius: 3px;
background: rgba(0, 20, 20, 0.2);
border-width: 0px;
border-top: transparent;
border-color: #f00;
color: #fee;
}
/* Custom Buttons */
.userbutton {
background: rgba(20,0,0, 0.15);
}
.userbuttonlabel {
color: #222;
font-size: 12px;
}
.userbutton:hover {
background: rgba(20, 0, 0, 0.2);
}
.userbuttonlabel:hover {
color: #111;
}
button.buttonState1 {
background: rgba(20,0,0,0.5);
}
.userbuttonlabel.buttonState1 {
color: #fff;
}
button.buttonState1:hover {
background: rgba(20,0,0, 0.4);
}
.userbuttonlabel.buttonState1:hover {
color: #111;
}
button.buttonState2 {
background: rgba(255,255,255,0.3);
}
.userbuttonlabel.buttonState2 {
color: #111;
}
button.buttonState2:hover {
background: rgba(20,0,0, 0.3);
}
.userbuttonlabel.buttonState2:hover {
color: #000;
}
/* Images */
image.deadd-noti-center.notification.image {
margin-left: 20px;
}

View File

@@ -1,263 +0,0 @@
### Margins for notification-center/notifications
margin-top: 0
margin-right: 0
### Margins for notification-center
margin-bottom: 0
### Width of the notification center/notifications in pixels.
width: 360
### Command to run at startup. This can be used to setup
### button states.
# startup-command: deadd-notification-center-startup
### Monitor on which the notification center/notifications will be
### printed. If "follow-mouse" is set true, this does nothing.
monitor: 0
### If true, the notification center/notifications will open on the
### screen, on which the mouse is. Overrides the "monitor" setting.
follow-mouse: false
notification-center:
### Margin at the top/right/bottom of the notification center in
### pixels. This can be used to avoid overlap between the notification
### center and bars such as polybar or i3blocks.
margin-top: 40
# margin-right: 0
# margin-bottom: 0
### Width of the notification center in pixels.
# width: 500
### Monitor on which the notification center will be printed. If
### "follow-mouse" is set true, this does nothing.
# monitor: 0
### If true, the notification center will open on the screen, on which
### the mouse is. Overrides the "monitor" setting.
# follow-mouse: false
### Notification center closes when the mouse leaves it
hide-on-mouse-leave: true
### If newFirst is set to true, newest notifications appear on the top
### of the notification center. Else, notifications stack, from top to
### bottom.
new-first: true
### If true, the transient field in notifications will be ignored,
### thus the notification will be persisted in the notification
### center anyways
ignore-transient: false
### Custom buttons in notification center
buttons:
### Numbers of buttons that can be drawn on a row of the notification
### center.
# buttons-per-row: 5
### Height of buttons in the notification center (in pixels).
# buttons-height: 60
### Horizontal and vertical margin between each button in the
### notification center (in pixels).
# buttons-margin: 2
### Button actions and labels. For each button you must specify a
### label and a command.
actions:
# - label: VPN
# command: "sudo vpnToggle"
# - label: Bluetooth
# command: bluetoothToggle
# - label: Wifi
# command: wifiToggle
# - label: Screensaver
# command: screensaverToggle
# - label: Keyboard
# command: keyboardToggle
notification:
### If true, markup (<u>, <i>, <b>, <a>) will be displayed properly
use-markup: true
### If true, html entities (&#38; for &, &#37; for %, etc) will be
### parsed properly. This is useful for chromium-based apps, which
### tend to send these in notifications.
parse-html-entities: true
dbus:
### If noti-closed messages are enabled, the sending application
### will know that a notification was closed/timed out. This can
### be an issue for certain applications, that overwrite
### notifications on status updates (e.g. Spotify on each
### song). When one of these applications thinks, the notification
### has been closed/timed out, they will not overwrite existing
### notifications but send new ones. This can lead to redundant
### notifications in the notification center, as the close-message
### is send regardless of the notification being persisted.
send-noti-closed: false
app-icon:
### If set to true: If no icon is passed by the app_icon parameter
### and no application "desktop-entry"-hint is present, deadd will
### try to guess the icon from the application name (if present).
guess-icon-from-name: true
### The display size of the application icons in the notification
### pop-ups and in the notification center
icon-size: 20
image:
### The maximal display size of images that are part of
### notifications for notification pop-ups and in the notification
### center
size: 100
### The margin around the top, bottom, left, and right of
### notification images.
margin-top: 15
margin-bottom: 15
margin-left: 15
margin-right: 0
### Apply modifications to certain notifications:
### Each modification rule needs a "match" and either a "modify" or
### a "script" entry.
modifications:
### Match:
### Matches the notifications against these rules. If all of the
### values (of one modification rule) match, the "modify"/"script"
### part is applied.
# - match:
### Possible match criteria:
# title: "Notification title"
# body: "Notification body"
# time: "12:44"
# app-name: "App name"
# urgency: "low" # "low", "normal" or "critical"
# modify:
### Possible modifications
# title: "abc"
# body: "abc"
# app-name: "abc"
# app-icon: "file:///abc.png"
### The timeout has three special values:
### timeout: 0 -> don't time out at all
### timeout: -1 -> use default timeout
### timeout: 1 -> don't show as pop-up
### timeout: >1 -> milliseconds until timeout
# timeout: 1
# margin-right: 10
# margin-top: 10
# image: "file:///abc.png"
# image-size: 10
# transient: true
# send-noti-closed: false
### Remove action buttons from notifications
# remove-actions: true
### Set the action-icons hint to true, action labels will then
### be intergreted as GTK icon names
# action-icons: true
### List of actions, where the even elements (0, 2, ...) are the
### action name and the odd elements are the label
# actions:
# - previous
# - media-skip-backward
# - play
# - media-playback-start
# - next
# - media-skip-forward
### Action commands, where the keys (e.g. "play") is the action
### name and the value is a program call that should be executed
### on action. Prevents sending of the action to the application.
# action-commands:
# play: playerctl play-pause
# previous: playerctl previous
# next: playerctl next
### Add a class-name to the notification container, that can be
### used for specific styling of notifications using the
### deadd.css file
# class-name: "abc"
# - match:
# app-name: "Chromium"
### Instead of modifying a notification directly, a script can be
### run, which will receive the notification as JSON on STDIN. It
### is expected to return JSON/YAML configuration that defines the
### modifications that should be applied. Minimum complete return
### value must be '{"modify": {}, "match": {}}'. Always leave the "match"
### object empty (technical reasons, i.e. I am lazy).
# script: "linux-notification-center-parse-chromium"
- match:
app-name: "Spotify"
modify:
image-size: 80
timeout: 1
send-noti-closed: true
class-name: "Spotify"
action-icons: true
actions:
- previous
- media-skip-backward
- play
- media-playback-start
- next
- media-skip-forward
action-commands:
play: playerctl play-pause
previous: playerctl previous
next: playerctl next
# - match:
# title: Bildschirmhelligkeit
# modify:
# image-size: 60
popup:
### Default timeout used for notifications in milli-seconds. This can
### be overwritten with the "-t" option (or "--expire-time") of the
### notify-send command.
default-timeout: 10000
### Margin above/right/between notifications (in pixels). This can
### be used to avoid overlap between notifications and a bar such as
### polybar or i3blocks.
margin-top: 50
margin-right: 50
margin-between: 20
### Defines after how many lines of text the body will be truncated.
### Use 0 if you want to disable truncation.
max-lines-in-body: 3
### Determines whether the GTK widget that displays the notification body
### in the notification popup will be hidden when empty. This is especially
### useful for transient notifications that display a progress bar.
# hide-body-if-empty: false
### Monitor on which the notifications will be
### printed. If "follow-mouse" is set true, this does nothing.
# monitor: 0
### If true, the notifications will open on the
### screen, on which the mouse is. Overrides the "monitor" setting.
# follow-mouse: false
click-behavior:
### The mouse button for dismissing a popup. Must be either "mouse1",
### "mouse2", "mouse3", "mouse4", or "mouse5"
dismiss: mouse1
### The mouse button for opening a popup with the default action.
### Must be either "mouse1", "mouse2", "mouse3", "mouse4", or "mouse5"
default-action: mouse3

View File

@@ -1,17 +0,0 @@
# docs are via README only:
# - <https://github.com/phuhl/linux_notification_center>
# reload config:
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:reloadStyle`
# toggle visibility:
# - `kill -s USR1 $(pidof deadd-notification-center)`
# clear notifications:
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=string:type:clearInCenter`
# set state of user button 0 to "highlighted" (true)
# - `notify-send a --hint=boolean:deadd-notification-center:true --hint=int:id:0 --hint=boolean:state:true --hint=type:string:buttons`
{ ... }:
{
sane.programs.deadd-notification-center = {
fs.".config/deadd/deadd.css".symlink.target = ./deadd.css;
fs.".config/deadd/deadd.yml".symlink.target = ./deadd.yml;
};
}

View File

@@ -5,31 +5,25 @@
./abaddon.nix ./abaddon.nix
./aerc.nix ./aerc.nix
./alacritty.nix ./alacritty.nix
./alsa-ucm-conf
./animatch.nix ./animatch.nix
./assorted.nix ./assorted.nix
./audacity.nix ./audacity.nix
./bemenu.nix ./bemenu.nix
./blast-ugjka
./bonsai.nix ./bonsai.nix
./brave.nix ./brave.nix
./bubblewrap.nix ./bubblewrap.nix
./calls.nix ./calls.nix
./cantata.nix ./cantata.nix
./catt.nix ./catt.nix
./celeste64.nix
./chatty.nix ./chatty.nix
./conky ./conky
./cozy.nix ./cozy.nix
./dconf.nix ./dconf.nix
./deadd-notification-center
./dialect.nix ./dialect.nix
./dino.nix ./dino.nix
./dissent.nix
./element-desktop.nix ./element-desktop.nix
./epiphany.nix ./epiphany.nix
./evince.nix ./evince.nix
./fcitx5.nix
./feedbackd.nix ./feedbackd.nix
./firefox.nix ./firefox.nix
./firejail.nix ./firejail.nix
@@ -51,6 +45,7 @@
./gpodder.nix ./gpodder.nix
./grimshot.nix ./grimshot.nix
./gthumb.nix ./gthumb.nix
./gtkcord4.nix
./handbrake.nix ./handbrake.nix
./helix.nix ./helix.nix
./imagemagick.nix ./imagemagick.nix
@@ -58,7 +53,6 @@
./kdenlive.nix ./kdenlive.nix
./komikku.nix ./komikku.nix
./koreader ./koreader
./less.nix
./libreoffice.nix ./libreoffice.nix
./lemoa.nix ./lemoa.nix
./loupe.nix ./loupe.nix
@@ -67,7 +61,7 @@
./mepo.nix ./mepo.nix
./mimeo ./mimeo
./mopidy.nix ./mopidy.nix
./mpv ./mpv.nix
./msmtp.nix ./msmtp.nix
./nautilus.nix ./nautilus.nix
./neovim.nix ./neovim.nix
@@ -87,10 +81,7 @@
./rhythmbox.nix ./rhythmbox.nix
./ripgrep.nix ./ripgrep.nix
./rofi ./rofi
./sane-input-handler
./sane-screenshot.nix
./sane-scripts.nix ./sane-scripts.nix
./schlock.nix
./sfeed.nix ./sfeed.nix
./signal-desktop.nix ./signal-desktop.nix
./splatmoji.nix ./splatmoji.nix
@@ -103,10 +94,8 @@
./supertuxkart.nix ./supertuxkart.nix
./sway ./sway
./sway-autoscaler ./sway-autoscaler
./swayidle.nix
./swaylock.nix ./swaylock.nix
./swaynotificationcenter ./swaynotificationcenter.nix
./sysvol.nix
./tangram.nix ./tangram.nix
./tor-browser.nix ./tor-browser.nix
./tuba.nix ./tuba.nix
@@ -119,19 +108,19 @@
./wireplumber.nix ./wireplumber.nix
./wireshark.nix ./wireshark.nix
./wob ./wob
./wvkbd.nix
./xarchiver.nix ./xarchiver.nix
./xdg-desktop-portal.nix ./xdg-desktop-portal.nix
./xdg-desktop-portal-gtk.nix ./xdg-desktop-portal-gtk.nix
./xdg-desktop-portal-wlr.nix ./xdg-desktop-portal-wlr.nix
./xdg-terminal-exec.nix
./xdg-utils.nix ./xdg-utils.nix
./zathura.nix
./zeal.nix ./zeal.nix
./zecwallet-lite.nix ./zecwallet-lite.nix
./zsh ./zsh
]; ];
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)? config = {
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*"; # XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
};
} }

View File

@@ -5,9 +5,6 @@
sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics sandbox.wrapperType = "inplace"; # share/search_providers/ calls back into the binary, weird wrap semantics
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.extraHomePaths = [
".config/dconf" # won't start without it
];
suggestedPrograms = [ "dconf" ]; #< to persist settings suggestedPrograms = [ "dconf" ]; #< to persist settings
packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: { packageUnwrapped = pkgs.dialect.overrideAttrs (upstream: {

View File

@@ -46,6 +46,7 @@ in
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications

View File

@@ -17,6 +17,7 @@
]; ];
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications

View File

@@ -12,13 +12,11 @@
sandbox.wrapperType = "inplace"; # /share/epiphany/default-bookmarks.rdf refers back to /share; dbus files to /libexec sandbox.wrapperType = "inplace"; # /share/epiphany/default-bookmarks.rdf refers back to /share; dbus files to /libexec
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; #< silently fails to start without it.
# default sandboxing breaks rendering in weird ways. sites are super zoomed in / not scaled. # default sandboxing breaks rendering in weird ways. sites are super zoomed in / not scaled.
# enabling DRI/DRM (as below) seems to fix that. # enabling DRI/DRM (as below) seems to fix that.
sandbox.whitelistDri = true; sandbox.whitelistDri = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
".config/dconf" # else will always prompt "make default browser?"
".config/epiphany" #< else it gets angry at launch ".config/epiphany" #< else it gets angry at launch
"tmp" "tmp"
]; ];

View File

@@ -1,123 +0,0 @@
# fcitx5 is an "input method", to e.g. allow typing CJK on qwerty.
# but i also misuse it to allow typing emoji on qwerty:
# - press `Super+backtick`
# - type something like "effort"
# - it should be underlined, at the least
# - if well supported (e.g. Firefox; also gtk4, alacritty on sway 1.10+), a drop-down fuzzy matcher will appear
# - press space
# - "effort" should be replaced by `(ง •̀_•́)ง`
#
## debugging
# - `fcitx5-diagnose`
#
## config/docs:
# - `fcitx5-configtool`, then check ~/.config/fcitx5 files
# - <https://fcitx-im.org/wiki/Fcitx_5>
# - <https://wiki.archlinux.org/title/Fcitx5>
# - theming: <https://wiki.archlinux.org/title/Fcitx5#Themes_and_appearance>
# - <https://en.wikipedia.org/wiki/Fcitx>
# - wayland specifics: <https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland>
# - quickphrase (emoji): <https://fcitx-im.org/wiki/QuickPhrase>
# - override phrases via `~/.config/fcitx/data/QuickPhrase.mb`
# - customize bindings via `fcitx5-configtool` > addons > QuickPhrase
# - theming:
# - nixpkgs has a few themes: `fcitx5-{material-color,nord,rose-pine}`
# - NUR has a few themes
# - <https://github.com/catppuccin/fcitx5>
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.fcitx5;
in
{
sane.programs.fcitx5 = {
packageUnwrapped = pkgs.fcitx5-with-addons.override {
addons = with pkgs; [
# fcitx5-mozc # japanese input: <https://github.com/fcitx/mozc>
fcitx5-gtk # <https://github.com/fcitx/fcitx5-gtk>
];
};
sandbox.method = "bwrap";
sandbox.whitelistDbus = [ "user" ];
sandbox.whitelistWayland = true; # for `fcitx5-configtool, if nothing else`
sandbox.extraHomePaths = [
# ".config/fcitx"
".config/fcitx5"
".local/share/fcitx5"
];
fs.".config/fcitx5/conf/quickphrase.conf".symlink.text = ''
# Choose key modifier
Choose Modifier=None
# Enable Spell check
Spell=True
FallbackSpellLanguage=en
[TriggerKey]
# defaults: Super+grave, Super+semicolon
# gtk apps use ctrl+period, so super+period is a nice complement
0=Super+grave
1=Super+semicolon
2=Super+period
'';
fs.".config/fcitx5/conf/classicui.conf".symlink.text = ''
Theme=sane
Font="Sans 20"
Vertical Candidate List=True
'';
fs.".local/share/fcitx5/themes/sane/theme.conf".symlink.text = ''
# i omit several keys, especially the ones which don't seem to do much.
# for a theme which uses many more options, see:
# - <https://github.com/catppuccin/fcitx5/blob/main/src/catppuccin-mocha/theme.conf>
[Metadata]
Name=sane
ScaleWithDPI=True
[InputPanel]
NormalColor=#d8d8d8
HighlightCandidateColor=#FFFFFF
HighlightColor=#FFFFFF
HighlightBackgroundColor=#1f5e54
[InputPanel/Background]
Color=#1f5e54
[InputPanel/Highlight]
Color=#418379
[InputPanel/Highlight/Margin]
Left=20
Right=20
Top=7
Bottom=7
[InputPanel/TextMargin]
Left=20
Right=20
Top=6
Bottom=6
'';
services.fcitx5 = {
description = "fcitx5: input method (IME) for emoji/internationalization";
after = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
ExecStart="${cfg.package}/bin/fcitx5";
Type = "simple";
Restart = "always";
RestartSec = "10s";
};
};
env.XMODIFIERS = "@im=fcitx";
# setting IM_MODULE is generally not required on wayland, but can be used to override the toolkit's own dialogs with our own.
# env.GTK_IM_MODULE = "fcitx";
# enable if you want them:
# env.QT_IM_MODULE = "fcitx";
# env.QT_PLUGIN_PATH = [ "${cfg.package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
# env.SDL_IM_MODULE = "fcitx";
# env.GLFW_IM_MODULE = "ibus"; # for KiTTY, as per <https://wiki.archlinux.org/title/Fcitx5#Integration>
};
}

View File

@@ -25,6 +25,7 @@ in
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; sandbox.whitelistDbus = [ "user" ];
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;

View File

@@ -234,7 +234,7 @@ in
sane.programs.firefox = { sane.programs.firefox = {
inherit packageUnwrapped; inherit packageUnwrapped;
sandbox.method = "bwrap"; # landlock works, but requires all of /proc to be linked sandbox.method = "bwrap"; # landlock works, but requires all of /proc to be linked
sandbox.wrapperType = "inplace"; # trivial package; cheap enough to wrap inplace sandbox.wrapperType = "inplace"; # probably wrappedDerivation could work too.
sandbox.net = "all"; sandbox.net = "all";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # mpris sandbox.whitelistDbus = [ "user" ]; # mpris
@@ -322,8 +322,6 @@ in
defaultPref("widget.use-xdg-desktop-portal.mime-handler", 1); defaultPref("widget.use-xdg-desktop-portal.mime-handler", 1);
defaultPref("widget.use-xdg-desktop-portal.open-uri", 1); defaultPref("widget.use-xdg-desktop-portal.open-uri", 1);
defaultPref("browser.toolbars.bookmarks.visibility", "never");
// auto-open mpv:// URIs without prompting. // auto-open mpv:// URIs without prompting.
// can do this with other protocols too (e.g. matrix?). see about:config for common handlers. // can do this with other protocols too (e.g. matrix?). see about:config for common handlers.
defaultPref("network.protocol-handler.external.mpv", true); defaultPref("network.protocol-handler.external.mpv", true);

View File

@@ -1,10 +1,6 @@
# to preview fonts: # to preview fonts:
# - `font-manager` (gui) # - `font-manager` (gui)
# - useful to determine official name; codepoint support # - useful to determine official name; codepoint support
# docs:
# - <https://slatecave.net/notebook/fontconfig/>
# debugging:
# - `fc-conflist` -> show all config files loaded
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
# nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji. # nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji.
@@ -34,12 +30,13 @@ in
{ {
sane.programs.fontconfig = { sane.programs.fontconfig = {
sandbox.method = "bwrap"; # TODO:sandbox: untested sandbox.method = "bwrap"; # TODO:sandbox: untested
sandbox.wrapperType = "wrappedDerivation";
sandbox.autodetectCliPaths = "existingOrParent"; #< this might be overkill; or, how many programs reference fontconfig internally? sandbox.autodetectCliPaths = "existingOrParent"; #< this might be overkill; or, how many programs reference fontconfig internally?
# persist.byStore.plaintext = [ persist.byStore.plaintext = [
# # < 10 MiB. however, nixos generates its own fontconfig cache at build time now. # < 10 MiB
# ".cache/fontconfig" ".cache/fontconfig"
# ]; ];
}; };
fonts = lib.mkIf config.sane.programs.fontconfig.enabled { fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
@@ -47,8 +44,8 @@ in
fontconfig.defaultFonts = { fontconfig.defaultFonts = {
emoji = [ emoji = [
"Noto Color Emoji" "Noto Color Emoji"
# "Font Awesome 6 Free" "Font Awesome 6 Free"
# "Font Awesome 6 Brands" "Font Awesome 6 Brands"
]; ];
monospace = [ monospace = [
"Hack Nerd Font Propo" "Hack Nerd Font Propo"
@@ -70,7 +67,7 @@ in
# TODO: reduce this font set. # TODO: reduce this font set.
# - probably need only one of dejavu/freefont/liberation # - probably need only one of dejavu/freefont/liberation
dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only) dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only)
# font-awesome # 2 MiB; Font Awesome 6 {Free,Brands} font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
freefont_ttf # 11 MiB; Free{Mono,Sans,Serif} freefont_ttf # 11 MiB; Free{Mono,Sans,Serif}
gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts
# hack-font # 1 MiB; Hack; also available as a NerdFonts # hack-font # 1 MiB; Hack; also available as a NerdFonts

View File

@@ -28,6 +28,7 @@ in
# packageUnwrapped = pkgs.fractal-next; # packageUnwrapped = pkgs.fractal-next;
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications
@@ -59,9 +60,9 @@ in
persist.byStore.private = [ persist.byStore.private = [
# XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>. # XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>.
# ".local/share/hack" # for debug-like builds ".local/share/hack" # for debug-like builds
# ".local/share/stable" # for normal releases ".local/share/stable" # for normal releases
".local/share/fractal" # for version 5+ ".local/share/fractal" # for version 5+, i think?
]; ];
suggestedPrograms = [ "gnome-keyring" ]; suggestedPrograms = [ "gnome-keyring" ];

View File

@@ -3,6 +3,7 @@
{ {
sane.programs.frozen-bubble = { sane.programs.frozen-bubble = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; # net play sandbox.net = "clearnet"; # net play
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -9,6 +9,7 @@
{ {
sane.programs.g4music = { sane.programs.g4music = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # mpris sandbox.whitelistDbus = [ "user" ]; # mpris
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -4,6 +4,7 @@
packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus"; packageUnwrapped = pkgs.linkIntoOwnPackage pkgs.glib "bin/gdbus";
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access sandbox.whitelistDbus = [ "user" ]; #< XXX: maybe future users will also want system access
}; };
} }

View File

@@ -20,6 +20,7 @@ in
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -19,6 +19,7 @@ in
''; '';
}); });
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistPwd = true; sandbox.whitelistPwd = true;
sandbox.autodetectCliPaths = true; # necessary for git-upload-pack sandbox.autodetectCliPaths = true; # necessary for git-upload-pack

View File

@@ -6,10 +6,10 @@ in
sane.programs.gnome-keyring = { sane.programs.gnome-keyring = {
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring; packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-keyring;
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; sandbox.whitelistDbus = [ "user" ];
sandbox.extraRuntimePaths = [ sandbox.extraRuntimePaths = [
"keyring" #< only needs keyring/control, but has to *create* that. "keyring/control"
# "keyring/control"
]; ];
sandbox.capabilities = [ sandbox.capabilities = [
# ipc_lock: used to `mlock` the secrets so they don't get swapped out. # ipc_lock: used to `mlock` the secrets so they don't get swapped out.
@@ -28,7 +28,7 @@ in
fs.".local/share/keyrings/default" = { fs.".local/share/keyrings/default" = {
file.text = "Default_keyring.keyring"; #< no trailing newline file.text = "Default_keyring.keyring"; #< no trailing newline
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ]; wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
wantedBeforeBy = [ #< don't create this as part of `multi-user.target` wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
"gnome-keyring.service" # TODO: sane.programs should declare this dependency for us "gnome-keyring.service" # TODO: sane.programs should declare this dependency for us
]; ];
@@ -43,7 +43,7 @@ in
lock-on-idle=false lock-on-idle=false
lock-after=false lock-after=false
''; '';
# wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ]; wantedBy = [ config.sane.fs."${config.sane.persist.stores.private.origin}".unit ];
wantedBeforeBy = [ #< don't create this as part of `multi-user.target` wantedBeforeBy = [ #< don't create this as part of `multi-user.target`
"gnome-keyring.service" "gnome-keyring.service"
]; ];
@@ -55,7 +55,6 @@ in
wantedBy = [ "graphical-session.target" ]; wantedBy = [ "graphical-session.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/gnome-keyring-daemon --start --foreground --components=secrets"; ExecStart = "${cfg.package}/bin/gnome-keyring-daemon --start --foreground --components=secrets";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -m 0700 -p %t/keyring";
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
RestartSec = "20s"; RestartSec = "20s";

View File

@@ -1,8 +1,8 @@
{ pkgs, ... }: { ... }:
{ {
sane.programs."gnome.gnome-maps" = { sane.programs."gnome.gnome-maps" = {
packageUnwrapped = pkgs.rmDbusServices pkgs.gnome.gnome-maps;
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< dbus files
sandbox.whitelistDri = true; # for perf sandbox.whitelistDri = true; # for perf
sandbox.whitelistDbus = [ sandbox.whitelistDbus = [
"system" # system is required for non-portal location services "system" # system is required for non-portal location services

View File

@@ -4,7 +4,7 @@
{ {
sane.programs."gnome.gnome-weather" = { sane.programs."gnome.gnome-weather" = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< /share/org.gnome.Weather/org.gnome.Weather file refers to bins by full path sandbox.wrapperType = "inplace";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.net = "clearnet"; sandbox.net = "clearnet";
suggestedPrograms = [ "dconf" ]; #< stores city/location settings suggestedPrograms = [ "dconf" ]; #< stores city/location settings

View File

@@ -34,6 +34,7 @@ in
{ {
sane.programs.go2tv = { sane.programs.go2tv = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.autodetectCliPaths = true; sandbox.autodetectCliPaths = true;
# for GUI invocation, allow the common media directories # for GUI invocation, allow the common media directories

View File

@@ -23,6 +23,7 @@ in {
}); });
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; # it won't launch without it, dunno exactly why. sandbox.whitelistDbus = [ "user" ]; # it won't launch without it, dunno exactly why.
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.net = "clearnet"; sandbox.net = "clearnet";

View File

@@ -15,6 +15,7 @@
"wl-clipboard" "wl-clipboard"
]; ];
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.whitelistDbus = [ "user" ]; sandbox.whitelistDbus = [ "user" ];
sandbox.autodetectCliPaths = "existingFileOrParent"; sandbox.autodetectCliPaths = "existingFileOrParent";

View File

@@ -3,10 +3,10 @@
# - notification sounds can be handled by swaync # - notification sounds can be handled by swaync
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.sane.programs.dissent; cfg = config.sane.programs.gtkcord4;
in in
{ {
sane.programs.dissent = { sane.programs.gtkcord4 = {
configOption = with lib; mkOption { configOption = with lib; mkOption {
default = {}; default = {};
type = types.submodule { type = types.submodule {
@@ -17,21 +17,22 @@ in
}; };
}; };
packageUnwrapped = pkgs.dissent.overrideAttrs (upstream: { packageUnwrapped = pkgs.gtkcord4.overrideAttrs (upstream: {
postConfigure = (upstream.postConfigure or "") + '' postConfigure = (upstream.postConfigure or "") + ''
# dissent uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring). # gtkcord4 uses go-keyring to interface with the org.freedesktop.secrets provider (i.e. gnome-keyring).
# go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`. # go-keyring hardcodes `login.keyring` as the keyring to store secrets in, instead of reading `~/.local/share/keyring/default`.
# `login.keyring` seems to be a special keyring preconfigured (by gnome-keyring) to encrypt everything to the user's password. # `login.keyring` seems to be a special keyring preconfigured (by gnome-keyring) to encrypt everything to the user's password.
# that's redundant with my fs-level encryption and makes the keyring less inspectable, # that's redundant with my fs-level encryption and makes the keyring less inspectable,
# so patch dissent to use Default_keyring instead. # so patch gtkcord4 to use Default_keyring instead.
# see: # see:
# - <https://github.com/diamondburned/dissent/issues/139> # - <https://github.com/diamondburned/gtkcord4/issues/139>
# - <https://github.com/zalando/go-keyring/issues/46> # - <https://github.com/zalando/go-keyring/issues/46>
substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \ substituteInPlace vendor/github.com/zalando/go-keyring/secret_service/secret_service.go \
--replace '"login"' '"Default_keyring"' --replace '"login"' '"Default_keyring"'
''; '';
}); });
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistAudio = true; sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications
@@ -51,17 +52,18 @@ in
]; ];
persist.byStore.private = [ persist.byStore.private = [
".cache/dissent" ".cache/gtkcord4"
".config/dissent" # empty? ".config/gtkcord4" # empty?
]; ];
services.dissent = { services.gtkcord4 = {
description = "dissent Discord client"; description = "gtkcord4 Discord client";
after = [ "graphical-session.target" ]; after = [ "graphical-session.target" ];
# partOf = [ "graphical-session.target" ];
wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ]; wantedBy = lib.mkIf cfg.config.autostart [ "graphical-session.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/dissent"; ExecStart = "${cfg.package}/bin/gtkcord4";
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
RestartSec = "20s"; RestartSec = "20s";

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.handbrake = { sane.programs.handbrake = {
sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir sandbox.method = "landlock"; #< also supports bwrap, but landlock ensures we don't write to non-mounted tmpfs dir
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.kdenlive = { sane.programs.kdenlive = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
"Music" "Music"
"Pictures/from" # e.g. Videos taken from my phone "Pictures/from" # e.g. Videos taken from my phone

View File

@@ -11,6 +11,7 @@
}); });
sandbox.method = "bwrap"; # TODO:sandbox untested sandbox.method = "bwrap"; # TODO:sandbox untested
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistDbus = [ "user" ]; # needs to connect to dconf via dbus sandbox.whitelistDbus = [ "user" ]; # needs to connect to dconf via dbus
sandbox.whitelistDri = true; #< required sandbox.whitelistDri = true; #< required

View File

@@ -46,6 +46,7 @@ in {
sane.programs.koreader = { sane.programs.koreader = {
packageUnwrapped = pkgs.koreader-from-src; packageUnwrapped = pkgs.koreader-from-src;
sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP sandbox.method = "bwrap"; # sandboxes fine under landlock too, except for FTP
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistDri = true; # reduces startup time and subjective page flip time sandbox.whitelistDri = true; # reduces startup time and subjective page flip time
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.lemoa = { sane.programs.lemoa = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.whitelistDbus = [ "user" ]; # for clicking links sandbox.whitelistDbus = [ "user" ]; # for clicking links
sandbox.whitelistDri = true; sandbox.whitelistDri = true;

View File

@@ -1,8 +0,0 @@
{ ... }:
{
sane.programs.less = {
sandbox.method = "bwrap";
sandbox.autodetectCliPaths = "existingFile";
env.PAGER = "less";
};
}

View File

@@ -12,6 +12,7 @@
})); }));
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.autodetectCliPaths = "parent"; sandbox.autodetectCliPaths = "parent";
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [

View File

@@ -10,13 +10,19 @@
# bwrap (loupe image viewer) doesn't like to run inside landlock # bwrap (loupe image viewer) doesn't like to run inside landlock
# "bwrap: failed to make / slave: Operation not permitted" # "bwrap: failed to make / slave: Operation not permitted"
sandbox.method = "bwrap"; # supports landlock or bwrap sandbox.method = "bwrap"; # supports landlock or bwrap
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDri = true; sandbox.whitelistDri = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.whitelistDbus = [ "user" ]; #< so that it can in theory open the image viewer using fdo portal... but it doesn't :| sandbox.whitelistDbus = [ "user" ]; #< so that it can in theory open the image viewer using fdo portal... but it doesn't :|
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
".config/dconf" #< else it segfaults during post-process
# ".config/megapixels" # ".config/megapixels"
# ".config/xcb"
# ".xcb"
".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff ".local/share/applications" #< needed for viewing photos, until i can sort out the portal stuff
# ".local/share/icons"
# ".icons" #< actually needed!
# ".themes"
# ".nix-profile"
".cache/mesa_shader_cache" # loads way faster ".cache/mesa_shader_cache" # loads way faster
"tmp" "tmp"
"Pictures" #< TODO: make this Pictures/Photos and save photos there "Pictures" #< TODO: make this Pictures/Photos and save photos there

View File

@@ -5,6 +5,7 @@
{ {
sane.programs.mepo = { sane.programs.mepo = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd sandbox.net = "all"; # for tiles *and* for localhost comm to gpsd
sandbox.whitelistDri = true; sandbox.whitelistDri = true;
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -1,8 +1,6 @@
# mimeo is an exec dispatcher like xdg-open, but which allows mapping different URL regexes to different handlers. # mimeo is an exec dispatcher like xdg-open, but why allows mapping different URL regexes to different handlers.
# my setup sets mimeo as the default http/https handler, # my setup sets mimeo as the default http/https handler,
# and from there it dispatches specialized rules, falling back to the original http/https handler if no URL specialization exists # and from there it dispatches specialized rules, falling back to the original http/https handler if no URL specialization exists
#
# alternative to mimeo is jaro: <https://github.com/isamert/jaro>
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin { mimeo-open-desktop = pkgs.static-nix-shell.mkPython3Bin {

View File

@@ -0,0 +1,195 @@
# mpv docs:
# - <https://mpv.io/manual/master>
# - <https://github.com/mpv-player/mpv/wiki>
# curated mpv mods/scripts/users:
# - <https://github.com/stax76/awesome-mpv>
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.mpv;
in
{
sane.programs.mpv = {
configOption = with lib; mkOption {
default = {};
type = types.submodule {
options.vo = mkOption {
type = types.nullOr types.str;
default = null;
description = "--vo=FOO flag to pass to mpv";
};
};
};
packageUnwrapped = (pkgs.wrapMpv pkgs.mpv-unwrapped {
scripts = with pkgs.mpvScripts; [
mpris
uosc
# pkgs.mpv-uosc-latest
];
extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
# 2023/08/29: fixes an error where mpv on moby launches with the message
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
#
# this is likely a regression for mpv 0.36.0.
# the actual error message *appears* to come from the mesa library, but it's tough to trace.
#
# TODO(2023/12/03): remove once mesa 23.3.1 lands: <https://github.com/NixOS/nixpkgs/pull/265740>
#
# backend compatibility (2023/10/22):
# run with `--vo=help` to see a list of all output options.
# non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
# ? null Null video output
# A (default)
# A dmabuf-wayland Wayland dmabuf video output
# A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
# A vdpau VDPAU with X11
# F drm Direct Rendering Manager (software scaling)
# F gpu-next Video output based on libplacebo
# F vaapi VA API with X11
# F x11 X11 (software scaling)
# F xv X11/Xv
# U gpu Shader-based GPU Renderer
# W caca libcaca (terminal rendering)
# W sdl SDL 2.0 Renderer
# W wlshm Wayland SHM video output (software scaling)
"--add-flags" "--vo=${cfg.config.vo}"
];
}).overrideAttrs (base: {
buildCommand = base.buildCommand + ''
# runHook postFixup to allow sandbox wrappers to wrap the binaries
runHook postFixup
'';
});
sandbox.method = "bwrap";
sandbox.autodetectCliPaths = true;
sandbox.net = "all";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; #< mpris
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [
".config/mpv" #< else mpris plugin crashes on launch
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
"Books/local"
"Books/servo"
"Music"
"Videos/local"
"Videos/servo"
];
persist.byStore.plaintext = [
# for `watch_later`
".local/state/mpv"
];
fs.".config/mpv/input.conf".symlink.text = let
execInTerm = "${pkgs.xdg-terminal-exec}/bin/xdg-terminal-exec";
in ''
# docs:
# - <https://mpv.io/manual/master/#list-of-input-commands>
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
# - properties: <https://mpv.io/manual/master/#property-list>
# let volume/power keys be interpreted by the system.
# this is important for sxmo.
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
POWER ignore
VOLUME_UP ignore
VOLUME_DOWN ignore
# uosc menu
# text after the shebang is parsed by uosc to construct the menu and names
menu script-binding uosc/menu
s script-binding uosc/subtitles #! Subtitles
a script-binding uosc/audio #! Audio tracks
q script-binding uosc/stream-quality #! Stream quality
p script-binding uosc/items #! Playlist
c script-binding uosc/chapters #! Chapters
> script-binding uosc/next #! Navigation > Next
< script-binding uosc/prev #! Navigation > Prev
o script-binding uosc/open-file #! Navigation > Open file
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
# script-binding uosc/audio-device #! Utils > Audio devices
# script-binding uosc/editions #! Utils > Editions
ctrl+s async screenshot #! Utils > Screenshot
alt+i script-binding uosc/keybinds #! Utils > Key bindings
O script-binding uosc/show-in-directory #! Utils > Show in directory
# script-binding uosc/open-config-directory #! Utils > Open config directory
# set pause yes; run ${execInTerm} go2tv -v "''${stream-open-filename}" #! Cast
# set pause yes; run ${execInTerm} go2tv -u "''${stream-open-filename}" #! Cast (...) > Stream
# set pause yes; run go2tv #! Cast (...) > GUI
# TODO: unify "Cast" and "Cast (stream)" options above.
'';
fs.".config/mpv/mpv.conf".symlink.text = ''
save-position-on-quit=yes
keep-open=yes
# force GUI, even for tracks w/o album art
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
player-operation-mode=pseudo-gui
# use uosc instead (for On Screen Controls)
osc=no
# uosc provides its own seeking/volume indicators, so you also don't need this
osd-bar=no
# uosc will draw its own window controls if you disable window border
border=no
'';
fs.".config/mpv/script-opts/osc.conf".symlink.text = ''
# make the on-screen controls *always* visible
# unfortunately, this applies to full-screen as well
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
# if uosc is installed, this file is unused
visibility=always
'';
fs.".config/mpv/script-opts/uosc.conf".symlink.text = let
play_pause_btn = "cycle:play_arrow:pause:no=pause/yes=play_arrow";
rev_btn = "command:replay_10:seek -10";
fwd_btn = "command:forward_30:seek 30";
in ''
# docs:
# - <https://github.com/tomasklaen/uosc>
# - <https://github.com/tomasklaen/uosc/blob/main/src/uosc.conf>
# - <https://superuser.com/questions/1775550/add-new-buttons-to-mpv-uosc-ui>
timeline_style=bar
timeline_persistency=paused,audio
controls_persistency=paused,audio
volume_persistency=audio
volume_opacity=0.75
# speed_persistency=paused,audio
# vvv want a close button?
top_bar=always
top_bar_persistency=paused
controls=menu,<video>subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,space,${rev_btn},${play_pause_btn},${fwd_btn},space,speed:1.0,gap,<video>fullscreen
text_border=6.0
font_bold=yes
color=foreground=ff8080,background_text=ff8080
ui_scale=1.0
'';
# mime.priority = 200; # default = 100; 200 means to yield to other apps
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
mime.associations."audio/flac" = "mpv.desktop";
mime.associations."audio/mpeg" = "mpv.desktop";
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
mime.associations."video/mp4" = "mpv.desktop";
mime.associations."video/quicktime" = "mpv.desktop";
mime.associations."video/webm" = "mpv.desktop";
mime.associations."video/x-flv" = "mpv.desktop";
mime.associations."video/x-matroska" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
};
}

View File

@@ -1,3 +0,0 @@
# font size used by mpv's console (`); default 16
# font_size=28
scale=2

View File

@@ -1,166 +0,0 @@
# curated mpv mods/scripts/users:
# - <https://github.com/stax76/awesome-mpv>
# mpv docs:
# - <https://mpv.io/manual/master>
# - <https://github.com/mpv-player/mpv/wiki>
# extensions i use:
# - <https://github.com/jonniek/mpv-playlistmanager>
# other extensions that could be useful:
# - list: <https://github.com/stax76/awesome-mpv>
# - list: <https://nudin.github.io/mpv-script-directory/>
# - browse DLNA shares: <https://github.com/chachmu/mpvDLNA>
# - act as a DLNS renderer (sink): <https://github.com/xfangfang/Macast>
# debugging:
# - enter console by pressing backtick.
# > `set volume 50` -> sets application volume to 50%
# > `set ao-volume 50` -> sets system-wide volume to 50%
# > `show-text "vol: ${volume}"` -> get the volume
# - show script output by running mpv with `--msg-level=all=trace`
# - and then just `print(...)` from lua & it'll show in terminal
# - invoke mpv with `--no-config` to have it not read ~/.config/mpv/*
# - press `i` to show decoder info
#
# usage tips:
# - `<` or `>` to navigate prev/next-file-in-folder (uosc)
# - shift+enter to view the playlist, then arrow-keys to navigate (mpv-playlistmanager)
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.mpv;
uosc = pkgs.mpvScripts.uosc.overrideAttrs (upstream: {
# patch so that the volume control corresponds to `ao-volume`, i.e. the system-wide volume.
# this is particularly nice for moby, because it avoids the awkwardness that system volume
# is hard to adjust while screen is on.
# note that only under alsa (`-ao=alsa`) does `ao-volume` actually correspond to system volume.
postPatch = (upstream.postPatch or "") + ''
substituteInPlace src/uosc/main.lua \
--replace-fail "mp.observe_property('volume'" "mp.observe_property('ao-volume'"
substituteInPlace src/uosc/elements/Volume.lua \
--replace-fail "mp.commandv('set', 'volume'" "mp.commandv('set', 'ao-volume'" \
--replace-fail "mp.set_property_native('volume'" "mp.set_property('ao-volume'"
# `ao-volume` isn't actually an observable property.
# as of 2024/03/02, they *may* be working on that:
# - <https://github.com/mpv-player/mpv/pull/13604#issuecomment-1971665736>
# in the meantime, just query the volume every tick (i.e. frame).
# alternative is mpv's JSON IPC feature, where i could notify its socket whenever pipewire volume changes.
cat <<EOF >> src/uosc/main.lua
function update_ao_volume()
local vol = mp.get_property('ao-volume')
if vol ~= nil then
vol = tonumber(vol)
if vol ~= state.volume then
set_state('volume', vol)
request_render()
end
end
end
-- tick seems to occur on every redraw (even when volume is hidden).
-- in practice: for every new frame of the source, or whenever the cursor is moved.
mp.register_event('tick', update_ao_volume)
-- if paused and cursor isn't moving, then `tick` isn't called. fallback to a timer.
mp.add_periodic_timer(2, update_ao_volume)
-- invoke immediately to ensure state.volume is non-nil
update_ao_volume()
if state.volume == nil then
state.volume = 0
end
EOF
'';
});
in
{
sane.programs.mpv = {
packageUnwrapped = with pkgs; wrapMpv mpv-unwrapped {
scripts = [
mpvScripts.mpris
mpvScripts.mpv-playlistmanager
uosc
# pkgs.mpv-uosc-latest
];
# extraMakeWrapperArgs = lib.optionals (cfg.config.vo != null) [
# # 2023/08/29: fixes an error where mpv on moby launches with the message
# # "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
# # audio still works, and controls, screenshotting, etc -- just not the actual rendering
# #
# # this is likely a regression for mpv 0.36.0.
# # the actual error message *appears* to come from the mesa library, but it's tough to trace.
# #
# # 2024/03/02: no longer necessary, with mesa 23.3.1: <https://github.com/NixOS/nixpkgs/pull/265740>
# #
# # backend compatibility (2023/10/22):
# # run with `--vo=help` to see a list of all output options.
# # non-exhaustive (W=works, F=fails, A=audio-only, U=audio+ui only (no video))
# # ? null Null video output
# # A (default)
# # A dmabuf-wayland Wayland dmabuf video output
# # A libmpv render API for libmpv (mpv plays the audio, but doesn't even render a window)
# # A vdpau VDPAU with X11
# # F drm Direct Rendering Manager (software scaling)
# # F gpu-next Video output based on libplacebo
# # F vaapi VA API with X11
# # F x11 X11 (software scaling)
# # F xv X11/Xv
# # U gpu Shader-based GPU Renderer
# # W caca libcaca (terminal rendering)
# # W sdl SDL 2.0 Renderer
# # W wlshm Wayland SHM video output (software scaling)
# "--add-flags" "--vo=${cfg.config.vo}"
# ];
};
suggestedPrograms = [
"blast-to-default"
"go2tv"
"xdg-terminal-exec"
];
sandbox.method = "bwrap";
sandbox.autodetectCliPaths = true;
sandbox.net = "all";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; #< mpris
sandbox.whitelistDri = true; #< mpv has excellent fallbacks to non-DRI, but DRI offers a good 30%-50% reduced CPU
sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [
".config/mpv" #< else mpris plugin crashes on launch
".local/share/applications" #< for xdg-terminal-exec (go2tv)
# it's common for album (or audiobook, podcast) images/lyrics/metadata to live adjacent to the primary file.
# CLI detection is too poor to pick those up, so expose the common media dirs to the sandbox to make that *mostly* work.
"Books/local"
"Books/servo"
"Music"
"Videos/gPodder"
"Videos/local"
"Videos/servo"
];
persist.byStore.plaintext = [
# for `watch_later`
".local/state/mpv"
];
fs.".config/mpv/scripts/sane/main.lua".symlink.target = ./sane-main.lua;
fs.".config/mpv/input.conf".symlink.target = ./input.conf;
fs.".config/mpv/mpv.conf".symlink.target = ./mpv.conf;
fs.".config/mpv/script-opts/osc.conf".symlink.target = ./osc.conf;
fs.".config/mpv/script-opts/console.conf".symlink.target = ./console.conf;
fs.".config/mpv/script-opts/uosc.conf".symlink.target = ./uosc.conf;
fs.".config/mpv/script-opts/playlistmanager.conf".symlink.target = ./playlistmanager.conf;
# mime.priority = 200; # default = 100; 200 means to yield to other apps
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
mime.associations."audio/flac" = "mpv.desktop";
mime.associations."audio/mpeg" = "mpv.desktop";
mime.associations."audio/x-opus+ogg" = "mpv.desktop";
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
mime.associations."video/mp4" = "mpv.desktop";
mime.associations."video/quicktime" = "mpv.desktop";
mime.associations."video/webm" = "mpv.desktop";
mime.associations."video/x-flv" = "mpv.desktop";
mime.associations."video/x-matroska" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtube.com/watch\?.*v=" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtube.com/v/" = "mpv.desktop";
mime.urlAssociations."^https?://(www.)?youtu.be/.+" = "mpv.desktop";
};
}

View File

@@ -1,37 +0,0 @@
# docs:
# - <https://mpv.io/manual/master/#list-of-input-commands>
# - script-binding: <https://mpv.io/manual/master/#command-interface-script-binding>
# - properties: <https://mpv.io/manual/master/#property-list>
# let volume/power keys be interpreted by the system.
# this is important for sxmo.
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
POWER ignore
VOLUME_UP ignore
VOLUME_DOWN ignore
# uosc menu
# text after the shebang is parsed by uosc to construct the menu and names
menu script-binding uosc/menu
s script-binding uosc/subtitles #! Subtitles
a script-binding uosc/audio #! Audio tracks
q script-binding uosc/stream-quality #! Stream quality
p script-binding uosc/items #! Playlist
c script-binding uosc/chapters #! Chapters
> script-binding uosc/next #! Navigation > Next
< script-binding uosc/prev #! Navigation > Prev
o script-binding uosc/open-file #! Navigation > Open file
# set video-aspect-override "-1" #! Utils > Aspect ratio > Default
# set video-aspect-override "16:9" #! Utils > Aspect ratio > 16:9
# set video-aspect-override "4:3" #! Utils > Aspect ratio > 4:3
# set video-aspect-override "2.35:1" #! Utils > Aspect ratio > 2.35:1
# script-binding uosc/audio-device #! Utils > Audio devices
# script-binding uosc/editions #! Utils > Editions
ctrl+s async screenshot #! Utils > Screenshot
alt+i script-binding uosc/keybinds #! Utils > Key bindings
O script-binding uosc/show-in-directory #! Utils > Show in directory
# script-binding uosc/open-config-directory #! Utils > Open config directory
ctrl+r script-binding sane/blast #! Audiocast
ctrl+t script-binding sane/go2tv-video #! Cast
# script-binding sane/go2tv-stream #! Cast (...) > Stream
# script-binding sane/go2tv-gui #! Cast (...) > GUI

View File

@@ -1,26 +0,0 @@
# write ~/.local/state/mpv/watch_later on exit, to allow resume
save-position-on-quit=yes
# identify resumed files by filename only, since i use so many symlinks and doubt mpv does well with that.
ignore-path-in-watch-later-config
# keep-open: don't exit on completion of last file in playlist
keep-open=yes
# seeking once at the end of the file causes auto-resume
keep-open-pause=no
# force GUI, even for tracks w/o album art
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
player-operation-mode=pseudo-gui
# use uosc instead (for On Screen Controls)
osc=no
# uosc provides its own seeking/volume indicators, so you also don't need this
osd-bar=no
# uosc will draw its own window controls if you disable window border
border=no
# ao=alsa so that uosc can work with ao-volume (see my uosc patch)
ao=alsa
# with `ao-volume`, the max actually is 100.
# to go higher you'll have to use the system's native controls.
volume-max=100

View File

@@ -1,5 +0,0 @@
# make the on-screen controls *always* visible
# unfortunately, this applies to full-screen as well
# - docs: <https://mpv.io/manual/master/#on-screen-controller-visibility>
# if uosc is installed, this file is unused
visibility=always

View File

@@ -1,4 +0,0 @@
# script docs: <https://github.com/jonniek/mpv-playlistmanager>
# auto-populate playlist with other files in the same directory, on launch.
loadfiles_on_start=yes

View File

@@ -1,34 +0,0 @@
function subprocess(in_terminal, args)
if in_terminal then
args = { "xdg-terminal-exec", table.unpack(args) }
end
mp.command_native({
name = "subprocess",
args = args,
detach = false,
capture_stdout = false,
capture_stderr = false,
-- capture_size=0,
passthrough_stdin = false,
playback_only = false,
})
end
function invoke_go2tv(in_terminal, args)
mp.commandv("set", "pause", "yes")
subprocess(in_terminal, { "go2tv", table.unpack(args) })
end
function invoke_go2tv_on_open_file(mode)
local path = mp.get_property("stream-open-filename");
return invoke_go2tv(true, { mode, path })
end
mp.add_key_binding(nil, "blast", function() subprocess(false, { "blast-to-default" }) end)
mp.add_key_binding(nil, 'go2tv-gui', function() invoke_go2tv(false, {}) end)
mp.add_key_binding(nil, 'go2tv-video', function() invoke_go2tv_on_open_file("-v") end)
mp.add_key_binding(nil, 'go2tv-stream', function() invoke_go2tv_on_open_file("-s") end)
-- uncomment for debugging:
-- if mpv fails to eval this script (e.g. syntax error), then it will fail to quit on launch
-- mp.command('quit')

View File

@@ -1,32 +0,0 @@
# docs:
# - <https://github.com/tomasklaen/uosc>
# - <https://github.com/tomasklaen/uosc/blob/main/src/uosc.conf>
# - <https://superuser.com/questions/1775550/add-new-buttons-to-mpv-uosc-ui>
timeline_style=bar
timeline_line_width=4
timeline_size=36
timeline_persistency=paused,audio
controls_persistency=paused,audio
volume_persistency=audio
# speed_persistency=paused,audio
# vvv want a close button?
top_bar=always
top_bar_persistency=paused,audio
controls=menu,<video>subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,space,command:replay_10:seek -10,cycle:play_arrow:pause:no=pause/yes=play_arrow,command:forward_30:seek 30,space,speed:1.0,gap,<video>fullscreen
# text_border: shadow to place around icons/text which is rendered over the video
text_border=5.0
# border_radius: rounding of volume slider, etc.
border_radius=5.0
font_scale=1.5
font_bold=yes
# refine=text_width: slightly better text rendering
refine=text_width
color=foreground=ff8080,background_text=ff8080
# N.B.: if `opacity=` is set non-empty, then ALL items must be specified (else they get 0 opacity).
# opacity values *must* be a multiple of 0.1
opacity=timeline=0.8,position=1,chapters=0.8,slider=0.8,slider_gauge=0.8,controls=0,speed=0.8,menu=1,submenu=0.4,border=1,title=0.8,tooltip=1,thumbnail=1,curtain=0.8,idle_indicator=0.8,audio_indicator=0.5,buffering_indicator=0.3,playlist_position=0.8
stream_quality_options=1440,1080,720,480,360,240,144

View File

@@ -1,16 +1,16 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
sane.programs."gnome.nautilus" = { sane.programs."gnome.nautilus" = {
# some of its dbus services don't even refer to real paths packageUnwrapped = pkgs.gnome.nautilus.overrideAttrs (orig: {
packageUnwrapped = pkgs.rmDbusServicesInPlace (pkgs.gnome.nautilus.overrideAttrs (orig: {
# enable the "Audio and Video Properties" pane. see: <https://nixos.wiki/wiki/Nautilus> # enable the "Audio and Video Properties" pane. see: <https://nixos.wiki/wiki/Nautilus>
buildInputs = orig.buildInputs ++ (with pkgs.gst_all_1; [ buildInputs = orig.buildInputs ++ (with pkgs.gst_all_1; [
gst-plugins-good gst-plugins-good
gst-plugins-bad gst-plugins-bad
]); ]);
})); });
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace";
sandbox.whitelistDbus = [ "user" ]; # for portals launching apps sandbox.whitelistDbus = [ "user" ]; # for portals launching apps
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [

View File

@@ -88,6 +88,7 @@ in
{ {
sane.programs.neovim = { sane.programs.neovim = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.autodetectCliPaths = "existingOrParent"; sandbox.autodetectCliPaths = "existingOrParent";
sandbox.whitelistWayland = true; # for system clipboard integration sandbox.whitelistWayland = true; # for system clipboard integration
# sandbox.whitelistPwd = true; # sandbox.whitelistPwd = true;

View File

@@ -11,6 +11,7 @@
}); });
sandbox.method = "firejail"; sandbox.method = "firejail";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.net = "vpn"; sandbox.net = "vpn";

View File

@@ -3,6 +3,7 @@
# provides `nix-locate`, backed by the manually run `nix-index` # provides `nix-locate`, backed by the manually run `nix-index`
sane.programs.nix-index = { sane.programs.nix-index = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
sandbox.extraPaths = [ sandbox.extraPaths = [
"/nix" "/nix"

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.notejot = { sane.programs.notejot = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
suggestedPrograms = [ "dconf" ]; #< else it can't persist notes suggestedPrograms = [ "dconf" ]; #< else it can't persist notes

View File

@@ -21,6 +21,7 @@ in
}; };
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.net = "clearnet"; sandbox.net = "clearnet";
secrets.".config/ntfy-sh/topic" = ../../../secrets/common/ntfy-sh-topic.bin; secrets.".config/ntfy-sh/topic" = ../../../secrets/common/ntfy-sh-topic.bin;

View File

@@ -3,6 +3,7 @@
{ {
sane.programs.open-in-mpv = { sane.programs.open-in-mpv = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; # for xdg-open/portals sandbox.whitelistDbus = [ "user" ]; # for xdg-open/portals
# taken from <https://github.com/Baldomo/open-in-mpv> # taken from <https://github.com/Baldomo/open-in-mpv>

View File

@@ -1,5 +1,5 @@
# administer with pw-cli, pw-mon, pw-top commands # administer with pw-cli, pw-mon, pw-top commands
{ config, lib, pkgs, ... }: { config, lib, ... }:
let let
cfg = config.sane.programs.pipewire; cfg = config.sane.programs.pipewire;
in in
@@ -18,10 +18,6 @@ in
sandbox.extraRuntimePaths = [ "/" ]; sandbox.extraRuntimePaths = [ "/" ];
sandbox.extraPaths = [ sandbox.extraPaths = [
"/dev/snd" "/dev/snd"
# desko/lappy don't need these, but moby complains if not present
"/dev/video0"
"/dev/video1"
"/dev/video2"
]; ];
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
# pulseaudio cookie # pulseaudio cookie
@@ -34,23 +30,10 @@ in
wantedBy = [ "graphical-session.target" ]; wantedBy = [ "graphical-session.target" ];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/pipewire"; ExecStart = "${cfg.package}/bin/pipewire";
ExecStartPost = pkgs.writeShellScript "pipewire-wait-started" ''
waitFor() {
while [ ! -e "$1" ]; do
sleep 1
done
}
waitFor "$XDG_RUNTIME_DIR/pipewire-0"
waitFor "$XDG_RUNTIME_DIR/pipewire-0-manager"
'';
ExecStopPost = ''rm -f "$XDG_RUNTIME_DIR/{pipewire-0,pipewire-0.lock,pipewire-0-manager,pipewire-0-manager.lock}"'';
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
RestartSec = "5s"; RestartSec = "5s";
}; };
# environment.PIPEWIRE_LOG_SYSTEMD = "false";
# environment.PIPEWIRE_DEBUG = "*:3,mod.raop*:5,pw.rtsp-client*:5";
}; };
services.pipewire-pulse = { services.pipewire-pulse = {
description = "pipewire-pulse: Pipewire compatibility layer for PulseAudio clients"; description = "pipewire-pulse: Pipewire compatibility layer for PulseAudio clients";
@@ -58,16 +41,6 @@ in
wantedBy = [ "pipewire.service" ]; wantedBy = [ "pipewire.service" ];
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/pipewire-pulse"; ExecStart = "${cfg.package}/bin/pipewire-pulse";
ExecStartPost = pkgs.writeShellScript "pipewire-pulse-wait-started" ''
waitFor() {
while [ ! -e "$1" ]; do
sleep 1
done
}
waitFor "$XDG_RUNTIME_DIR/pulse/native"
waitFor "$XDG_RUNTIME_DIR/pulse/pid"
'';
ExecStopPost = ''rm -f "$XDG_RUNTIME_DIR/pulse/{native,pid}"'';
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
RestartSec = "5s"; RestartSec = "5s";

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.planify = { sane.programs.planify = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; # for dconf? else it can't persist any tasks/notes sandbox.whitelistDbus = [ "user" ]; # for dconf? else it can't persist any tasks/notes
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;

View File

@@ -2,7 +2,7 @@
{ {
sane.programs.playerctl = { sane.programs.playerctl = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace"; #< /lib/pkgconfig/playerctl.pc refers to $out by full path sandbox.wrapperType = "inplace";
sandbox.whitelistDbus = [ "user" ]; # notifications sandbox.whitelistDbus = [ "user" ]; # notifications
services.playerctld = { services.playerctld = {

View File

@@ -3,6 +3,7 @@
sane.programs.portfolio-filemanager = { sane.programs.portfolio-filemanager = {
# this is all taken pretty directly from nautilus config # this is all taken pretty directly from nautilus config
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "inplace";
sandbox.whitelistDbus = [ "user" ]; # for portals launching apps sandbox.whitelistDbus = [ "user" ]; # for portals launching apps
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [

View File

@@ -2,6 +2,7 @@
{ {
sane.programs.ripgrep = { sane.programs.ripgrep = {
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.autodetectCliPaths = true; sandbox.autodetectCliPaths = true;
sandbox.whitelistPwd = true; sandbox.whitelistPwd = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [

View File

@@ -6,112 +6,33 @@
configuration { configuration {
modes: "combi"; modes: "combi";
font: "mono 20";
show-icons: true; show-icons: true;
combi-modes: "filebrowser,drun,run";
kb-accept-entry: "Return,KP_Enter,XF86PowerOff"; kb-accept-entry: "Return,KP_Enter,XF86PowerOff";
kb-row-up: "Up,XF86AudioRaiseVolume"; kb-row-up: "Up,XF86AudioRaiseVolume";
kb-row-down: "Down,XF86AudioLowerVolume"; kb-row-down: "Down,XF86AudioLowerVolume";
cache-dir: "~/.cache/rofi";
/* to position rofi to the top of the screen: */
/* location: 2; */
combi {
/* this is rendered in the filter box, here we disable it */
display-name: "";
}
/* combi-display-format: "{mode} {text}"; */
/* combi-display-format: "{text}"; */
combi-display-format: "{mode}{text}";
combi-modes: "filebrowser,drun";
drun {
display-name: " ";
}
drun-use-desktop-cache: true;
filebrowser { filebrowser {
/* directory: filebrowser starting directory. leave unset to start at the last directory. */ /* filebrowser starting directory */
/* directory: "/home"; */ directory: "/home";
/* display-name: text to prepend in combi mode */ /* display-name: text to prepend in combi mode */
display-name: "/"; display-name: "/";
/* `command` is the prefix to prepend (along with a space) *before* passing it off to `run-command` */ /* `command` is the prefix to prepend (along with a space) *before* passing it off to `run-command` */
command: "xdg-open"; command: "xdg-open";
directories-first: true;
/* sorting-method: name/atime/ctime/mtime */
sorting-method: "name";
show-hidden: false;
} }
drun {
display-name: " ";
}
run { run {
display-name: "run "; display-name: "run ";
} }
/* combi-display-format: "{mode} {text}"; */
/* combi-display-format: "{text}"; */
combi-display-format: "{mode}{text}";
/* launch applications via my own launcher, which directs them through to xdg-desktop-portal */ /* launch applications via my own launcher, which directs them through to xdg-desktop-portal */
run-command: "rofi-run-command '{app_id}.desktop' {cmd}"; run-command: "rofi-run-command '{app_id}.desktop' {cmd}";
drun-use-desktop-cache: true;
} }
/* theme */ @theme "gruvbox-light"
* {
/* my own variables */
bg: #1d1721; /* slight purple */
fg0: #d8d8d8; /* inactive text (light grey) */
fg1: #ffffff; /* active text (white) */
accent0: #1f5e54; /* darker but saturated teal */
accent1: #418379; /* teal (matches nixos-bg) */
accent2: #5b938a; /* brighter but muted teal */
/* map my variables to variables rofi uses internally */
background-color: var(accent0);
background: var(accent0);
/* foreground: non-alternating text, scrollbar, borders, separators */
foreground: var(fg0);
/* override derived styles */
alternate-active-background: var(accent0);
alternate-normal-background: var(accent0);
alternate-active-foreground: var(fg0);
alternate-normal-foreground: var(fg0);
border-color: var(accent0);
text-color: var(fg0);
selected-active-background: var(accent1);
selected-normal-background: var(accent1);
selected-active-foreground: var(fg1);
selected-normal-foreground: var(fg1);
separatorcolor: var(accent1);
}
entry {
placeholder: "";
text-color: var(fg1);
}
num-rows, num-filtered-rows {
text-color: var(fg0);
}
prompt, textbox-prompt-colon {
/* hide */
text-color: var(accent0);
}
scrollbar {
handle-color: var(accent2);
}
window {
/* rofi supports very complex calculations here */
/* one may even read environment variables (useful for knowing if screen is rotated?) */
/* `calc(... min 100%)` ensures it never overflows */
/* rofi is aware of the top bar (waybar) and any virtual keyboards,
* so e.g. height: 100% will occupy 100% of the height *not* allocated to bars/kbds.
* however with y-offset, it becomes possible to overflow */
width: calc(960 min 100%);
/* 520px @ font size 20 gives 13 rows + filter */
/* 482px @ font size 20 gives 12 rows + filter */
/* 446px @ font size 20 gives 11 rows + filter */
/* 90.5% @ font size 20, sway scale 2.0, moby in landscape mode: gives 7 rows + filter */
height: calc(446 min 90.5%);
/* anchor the *north* edge of the window at the *north* location of the screen */
anchor: north;
location: north;
/* 11.2% lines up nicely with Firefox */
y-offset: 11.2%;
}

View File

@@ -25,27 +25,28 @@
{ pkgs, ... }: { pkgs, ... }:
let let
rofi-unwrapped = pkgs.rofi-wayland-unwrapped.overrideAttrs (upstream: { rofi-unwrapped = pkgs.rofi-wayland-unwrapped.overrideAttrs (upstream: {
patches = (upstream.patches or []) ++ [ # my patches made for tip don't cleanly apply to stable, so advance the entire src
(pkgs.fetchpatch { src = pkgs.fetchFromGitea {
url = "https://git.uninsane.org/colin/rofi/commit/8e01fcd16f97f4c2a5bc63ade58c894a938f89d9.patch"; domain = "git.uninsane.org";
name = "run-{shell-,}command: expand `{app_id}` inside the template string"; owner = "colin";
hash = "sha256-DXafvvKrNyDOH11lpRdC2ljydb422ttY68oY5K3fKWo="; repo = "rofi";
}) fetchSubmodules = true;
(pkgs.fetchpatch { # rev = "dev-sane"; #< fetchFromGitea doesn't support tags (?)
url = "https://git.uninsane.org/colin/rofi/commit/249450a2b58c3cf7ced911cadb8c4c60d3315dd0.patch"; rev = "1edfceaeefa2cae971ae90dc55811f3b7592a1b4";
name = "filebrowser: include entries of d_type DT_UNKNOWN"; hash = "sha256-oIWLwec1LRsss12S92ebBWQk14FBJWc6QcYxzOU3eFI=";
hash = "sha256-gz3N4uo7IWzzqaPHHVhby/e9NbtzcFJRQwgdNYxO/Yw="; };
}) # patches = (upstream.patches or []) ++ [
]; # (pkgs.fetchpatch {
# url = "https://git.uninsane.org/colin/rofi/commit/d8bb0b9944ec1f3bf7479c9f127ec09d4198e87f.patch";
# name = "run-{shell-,}command: expand `{app_id}` inside the template string";
# hash = "sha256-XiZRvr+BARU7h3OPU0NUUEem3isnUVER69zucSqvNNk=";
# })
# ];
}); });
# rofi-emoji = pkgs.rofi-emoji.override { # rofi-emoji = pkgs.rofi-emoji.override {
# # plugins must be compiled against the same rofi they're loaded by # # plugins must be compiled against the same rofi they're loaded by
# inherit rofi-unwrapped; # inherit rofi-unwrapped;
# }; # };
# rofi-file-browser = pkgs.rofi-file-browser.override {
# # plugins must be compiled against the same rofi they're loaded by
# rofi = rofi-unwrapped;
# };
in in
{ {
sane.programs.rofi = { sane.programs.rofi = {
@@ -53,15 +54,8 @@ in
# it's actively maintained though, and more of an overlay than a true fork. # it's actively maintained though, and more of an overlay than a true fork.
packageUnwrapped = pkgs.rofi-wayland.override { packageUnwrapped = pkgs.rofi-wayland.override {
inherit rofi-unwrapped; inherit rofi-unwrapped;
plugins = [ # rofi-emoji: "insert" mode doesn't work; use a wrapper like `splatmoji` instead.
# rofi-[extended-]-file-browser: <https://github.com/marvinkreis/rofi-file-browser-extended> # plugins = [ rofi-emoji ];
# because the builtin rofi filebrowser only partially lists ~/Videos/servo/Shows, seemingly at random.
# but rofi-file-browser doesn't compile against my patched rofi (oops)
# rofi-file-browser
# rofi-emoji: "insert" mode doesn't work; use a wrapper like `splatmoji` instead.
# rofi-emoji
];
}; };
suggestedPrograms = [ suggestedPrograms = [
@@ -69,12 +63,11 @@ in
]; ];
sandbox.method = "bwrap"; sandbox.method = "bwrap";
sandbox.wrapperType = "wrappedDerivation";
sandbox.whitelistDbus = [ "user" ]; #< to launch apps via the portal sandbox.whitelistDbus = [ "user" ]; #< to launch apps via the portal
sandbox.whitelistWayland = true; sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [ sandbox.extraHomePaths = [
".local/share/applications" #< to locate .desktop files ".local/share/applications" #< to locate .desktop files
"Books/local"
"Books/servo"
"Music" "Music"
"Pictures/albums" "Pictures/albums"
"Pictures/cat" "Pictures/cat"
@@ -82,22 +75,18 @@ in
"Pictures/Photos" "Pictures/Photos"
"Pictures/Screenshots" "Pictures/Screenshots"
"Pictures/servo-macros" "Pictures/servo-macros"
"Videos/gPodder"
"Videos/local" "Videos/local"
"Videos/servo" "Videos/servo"
"knowledge" "knowledge"
"tmp" "tmp"
]; ];
sandbox.extraPaths = [
"/mnt/servo/media"
"/mnt/servo/playground"
];
fs.".config/rofi/config.rasi".symlink.target = ./config.rasi; fs.".config/rofi/config.rasi".symlink.target = ./config.rasi;
# redirect its default drun cache location
fs.".cache/rofi3.druncache".symlink.target = "rofi/rofi3.druncache";
fs.".cache/rofi-drun-desktop.cache".symlink.target = "rofi/rofi-drun-desktop.cache";
persist.byStore.cryptClearOnBoot = [ persist.byStore.cryptClearOnBoot = [
# this gets us a few things: # optional, for caching .desktop files rofi finds on disk (perf)
# - file browser remembers its last directory
# - caching of .desktop files (perf)
".cache/rofi" ".cache/rofi"
]; ];
}; };
@@ -106,47 +95,13 @@ in
packageUnwrapped = pkgs.static-nix-shell.mkBash { packageUnwrapped = pkgs.static-nix-shell.mkBash {
pname = "rofi-run-command"; pname = "rofi-run-command";
srcRoot = ./.; srcRoot = ./.;
pkgs = [ "sane-open-desktop" "xdg-utils" ]; pkgs = [ "glib" "xdg-utils" ];
}; };
sandbox.enable = false; #< trivial script, and all our deps are sandboxed sandbox.enable = false; #< trivial script, and all our deps are sandboxed
suggestedPrograms = [ suggestedPrograms = [
"sane-open-desktop" "gdbus"
"xdg-utils" "xdg-utils"
]; ];
}; };
sane.programs.rofi-snippets = {
packageUnwrapped = pkgs.static-nix-shell.mkBash {
pname = "rofi-snippets";
srcRoot = ./.;
pkgs = [
"gnused"
"rofi"
"wtype"
];
nativeBuildInputs = [
pkgs.copyDesktopItems
];
desktopItems = [
(pkgs.makeDesktopItem {
name = "rofi-snippets";
exec = "rofi-snippets";
desktopName = "rofi macro to insert common texts";
})
];
};
# if i could remove the sed, then maybe possible to not sandbox.
sandbox.method = "bwrap";
sandbox.whitelistWayland = true;
sandbox.extraHomePaths = [
".cache/rofi"
".config/rofi/config.rasi"
];
suggestedPrograms = [ "rofi" ];
fs.".config/rofi-snippets/public.txt".symlink.target = ./snippets.txt;
secrets.".config/rofi-snippets/private.txt" = ../../../../secrets/common/snippets.txt.bin;
};
} }

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i bash -p sane-open-desktop -p xdg-utils #!nix-shell -i bash -p glib -p xdg-utils
# use: # use:
# rofi-run-command <handler>.desktop [cmd [args ...]] # rofi-run-command <handler>.desktop [cmd [args ...]]
@@ -14,13 +14,11 @@ shift
binArgs=("$@") binArgs=("$@")
if [ "$desktop" != .desktop ]; then if [ "$desktop" != .desktop ]; then
# launching an app; the file browser position is no longer interesting: clear it so it opens in ~ next time. exec gdbus call --session --timeout 10 \
# better UX would be to manage this in the other branch: --dest org.freedesktop.portal.Desktop \
# - open in ~ by default, regardless of last directory --object-path /org/freedesktop/portal/desktop \
# - after launching a *file*, when that file is closed, re-open rofi in that file's directory. --method org.freedesktop.portal.DynamicLauncher.Launch \
# however, `xdg-open` and the `OpenFile` xdg-desktop-portal API don't give any obvious way to block for the app to close. "$desktop" {}
rm -f ~/.cache/rofi/rofi3.filebrowsercache
exec sane-open-desktop "$desktop"
elif [ "$binary" = "xdg-open" ]; then elif [ "$binary" = "xdg-open" ]; then
exec xdg-open "$@" exec xdg-open "$@"
fi fi

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p gnused -p rofi -p wtype
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
snippet=$(cat ~/.config/rofi-snippets/public.txt ~/.config/rofi-snippets/private.txt | \
rofi -dmenu | \
sed 's/ #.*$//')
wtype "$snippet"

View File

@@ -1,197 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.programs.sane-input-handler;
doExec = inputName: transitions: {
type = "exec";
command = [
"setsid" "-f" #< fork before invoking the input handler, else it runs synchronously
"sane-input-handler"
inputName
];
inherit transitions;
};
onDelay = ms: transitions: {
type = "delay";
delay_duration = ms * 1000000;
inherit transitions;
};
onEvent = eventName: transitions: {
type = "event";
event_name = eventName;
inherit transitions;
};
friendlyToBonsai = { trigger ? null, terminal ? false, timeout ? {}, power_pressed ? {}, power_released ? {}, voldown_pressed ? {}, voldown_released ? {}, volup_pressed ? {}, volup_released ? {} }@args:
if trigger != null then [
(doExec trigger (friendlyToBonsai (builtins.removeAttrs args ["trigger"])))
] else let
events = [ ]
++ (lib.optional (timeout != {}) (onDelay (timeout.ms or 400) (friendlyToBonsai (builtins.removeAttrs timeout ["ms"]))))
++ (lib.optional (power_pressed != {}) (onEvent "power_pressed" (friendlyToBonsai power_pressed)))
++ (lib.optional (power_released != {}) (onEvent "power_released" (friendlyToBonsai power_released)))
++ (lib.optional (voldown_pressed != {}) (onEvent "voldown_pressed" (friendlyToBonsai voldown_pressed)))
++ (lib.optional (voldown_released != {}) (onEvent "voldown_released" (friendlyToBonsai voldown_released)))
++ (lib.optional (volup_pressed != {}) (onEvent "volup_pressed" (friendlyToBonsai volup_pressed)))
++ (lib.optional (volup_released != {}) (onEvent "volup_released" (friendlyToBonsai volup_released)))
;
in assert terminal -> events == []; events;
# trigger ${button}_hold_N every `holdTime` ms until ${button} is released
recurseHold = button: { count ? 1, maxHolds ? 5, prefix ? "", holdTime ? 600, ... }@opts: lib.optionalAttrs (count <= maxHolds) {
"${button}_released".terminal = true; # end the hold -> back to root state
timeout = {
ms = holdTime;
trigger = "${prefix}${button}_hold_${builtins.toString count}";
} // (recurseHold button (opts // { count = count+1; }));
};
# trigger volup_tap_N or voldown_tap_N on every tap.
# if a volume button is held, then switch into `recurseHold`'s handling instead
volumeActions = { count ? 1, maxTaps ? 5, prefix ? "", timeout ? 600, ... }@opts: lib.optionalAttrs (count != maxTaps) {
volup_pressed = (recurseHold "volup" opts) // {
volup_released = {
trigger = "${prefix}volup_tap_${builtins.toString count}";
timeout.ms = timeout;
} // (volumeActions (opts // { count = count+1; }));
};
voldown_pressed = (recurseHold "voldown" opts) // {
voldown_released = {
trigger = "${prefix}voldown_tap_${builtins.toString count}";
timeout.ms = timeout;
} // (volumeActions (opts // { count = count+1; }));
};
};
in
{
sane.programs.sane-input-handler = {
configOption = with lib; mkOption{
default = {};
type = types.submodule {
options = {
devices = mkOption {
type = types.listOf types.str;
default = [
"0:1:Power_Button" #< Thinkpad power button
"1:1:AT_Translated_Set_2_keyboard" #< Thinkpad volume buttons (plus all its other buttons)
"0:0:axp20x-pek" #< Pinephone power button
"1:1:1c21800.lradc" #< Pinephone volume buttons
];
description = ''
list of devices which we should listen for special inputs from.
find these names with:
`swaymsg -t get_inputs --raw | jq 'map(.identifier)'`
'';
};
};
};
};
packageUnwrapped = pkgs.static-nix-shell.mkBash {
pname = "sane-input-handler";
srcRoot = ./.;
pkgs = {
inherit (pkgs) coreutils killall playerctl procps sane-open-desktop util-linux wireplumber;
sway = config.sane.programs.sway.package.sway-unwrapped;
};
};
suggestedPrograms = [
"bonsai"
"killall"
"playerctl"
"procps"
"sane-open-desktop"
"sway"
"wireplumber"
"wvkbd"
];
sandbox.method = "bwrap";
sandbox.whitelistAudio = true;
sandbox.whitelistDbus = [ "user" ]; #< to launch applications
sandbox.extraRuntimePaths = [ "sway-ipc.sock" ];
sandbox.extraConfig = [
"--sane-sandbox-keep-namespace" "pid"
];
};
# sane.programs.actkbd = {
# fs.".config/actkbd/actkbd.conf".symlink.text = ''
# 114:key::bonsaictl -e voldown_pressed
# 114:rel::bonsaictl -e voldown_released
# 115:key::bonsaictl -e volup_pressed
# 115:rel::bonsaictl -e volup_released
# # note that power might be on its own /dev/input/event* device separate from the volume buttons
# 116:key::bonsaictl -e power_pressed
# 116:rel::bonsaictl -e power_released
# '';
# services.actkbd = {
# # TODO: apparently i need one instance per /dev/input, which also means i need udev, etc.
# description = "actkbd: keyboard input mapping";
# after = [ "graphical-session.target" ];
# wantedBy = [ "graphical-session.target" ];
# serviceConfig = {
# ExecStart = "${config.sane.programs.actkbd.package}/bin/actkbd -c /home/colin/.config/actkbd/actkbd.conf";
# Type = "simple";
# Restart = "always";
# RestartSec = "5s";
# };
# };
# };
# TODO: duplicated sandboxing here is just ugly
sane.programs.bonsai.sandbox = lib.mkIf cfg.enabled (
builtins.removeAttrs cfg.sandbox [ "method" ] #< else infinite recursion
);
sane.programs.bonsai.config.transitions = lib.mkIf cfg.enabled (friendlyToBonsai {
# map sequences of "events" to an argument to pass to sane-input-handler
# map: power (short), power (short) x2, power (long)
power_pressed.timeout.ms = 900; # press w/o release. this is a long timeout because it's tied to the "kill window" action.
power_pressed.timeout.trigger = "powerhold";
power_pressed.power_released.timeout.trigger = "powerbutton_one";
power_pressed.power_released.timeout.ms = 300;
power_pressed.power_released.power_pressed.trigger = "powerbutton_two";
# map power (short) -> volup/voldown
power_pressed.power_released.volup_pressed.trigger = "powerbutton_volup";
power_pressed.power_released.voldown_pressed.trigger = "powerbutton_voldown";
# map: volume taps and holds
volup_pressed = (recurseHold "volup" {}) // {
# this either becomes volup_hold_* (via recurseHold, above) or:
# - a short volup_tap_1 followed by:
# - a *finalized* volup_1 (i.e. end of action)
# - more taps/holds, in which case we prefix it with `modal_<action>`
# to denote that we very explicitly entered this state.
#
# it's clunky: i do it this way so that voldown can map to keyboard/terminal in unlock mode
# but trigger media controls in screenoff
# in a way which *still* allows media controls if explicitly entered into via a tap on volup first
volup_released = (volumeActions { prefix = "modal_"; }) // {
trigger = "volup_tap_1";
timeout.ms = 300;
timeout.trigger = "volup_1";
};
};
voldown_pressed = (volumeActions {}).voldown_pressed // {
trigger = "voldown_start";
};
});
sane.programs.sway.config.extra_lines = lib.mkIf cfg.enabled (
''
# bindsym --input-device=... :
# i wish to route certain events both to bonsai and/or the application
# (rather, if there is an application which would receive them, send them, else re-route to bonsai).
# bindsym --input-device=* SWALLOWS all events: it sends them to bonsaid and NOT any gui app.
# bindsym --input-device=<dev> DOESN'T SWALLOW EVENTS: it sends them to BOTH places (yay!).
# however, this is considered a BUG. i am relying on a bug here that may be fixed in future sway versions:
# - <https://github.com/swaywm/sway/issues/6961>
# if so, migrate this to an evdev daemon, such as `evdevremapkeys` or `actkbd`
'' + lib.concatStringsSep "\n" (lib.forEach cfg.config.devices (dev: ''
bindsym --locked --input-device=${dev} --no-repeat XF86PowerOff exec bonsaictl -e power_pressed
bindsym --locked --input-device=${dev} --release XF86PowerOff exec bonsaictl -e power_released
bindsym --locked --input-device=${dev} --no-repeat XF86AudioRaiseVolume exec bonsaictl -e volup_pressed
bindsym --locked --input-device=${dev} --release XF86AudioRaiseVolume exec bonsaictl -e volup_released
bindsym --locked --input-device=${dev} --no-repeat XF86AudioLowerVolume exec bonsaictl -e voldown_pressed
bindsym --locked --input-device=${dev} --release XF86AudioLowerVolume exec bonsaictl -e voldown_released
''))
);
}

View File

@@ -1,226 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p coreutils -p killall -p playerctl -p procps -p sane-open-desktop -p sway -p util-linux -p wireplumber
# input map considerations
# - using compound actions causes delays.
# e.g. if volup->volup is a distinct action from volup, then single-volup action is forced to wait the maximum button delay.
# - actions which are to be responsive should therefore have a dedicated key.
# - a dedicated "kill" combo is important for unresponsive fullscreen apps, because appmenu doesn't show in those
# - although better may be to force appmenu to show over FS apps
# - bonsai mappings are static, so buttons can't benefit from non-compounding unless they're mapped accordingly for all lock states
# - this limitation could be removed, but with work
#
# example of a design which considers these things:
# - when unlocked:
# - volup toggle -> app menu
# - voldown press -> keyboard
# - voldown hold -> terminal
# - power x2 -> screenoff
# - hold power -> kill app
# - when locked:
# - volup tap -> volume up
# - volup hold -> media seek forward
# - voldown tap -> volume down
# - voldown hold -> media seek backward
# - power x1 -> screen on
# - power x2 -> play/pause media
# some trickiness allows for media controls in unlocked mode:
# - volup tap -> enter media mode
# - i.e. in this state, vol tap/hold is mapped to volume/seek
# - if, after entering media mode, no more taps occur, then we trigger the default app-menu action
# limitations/downsides:
# - power mappings means phone is artificially slow to unlock.
# - media controls when unlocked have quirks:
# - mashing voldown to decrease the volume will leave you with a toggled keyboard.
# - seeking backward isn't possible except by first tapping volup.
# increments to use for volume adjustment (in %)
VOL_INCR=5
KEYBOARD="${KEYBOARD:-wvkbd-mobintl}"
action="$1"
isTouchOn() {
# success if all touch inputs have their events enabled
swaymsg -t get_inputs --raw \
| jq --exit-status '. | map(select(.type == "touch")) | all(.libinput.send_events == "enabled")' \
> /dev/null
}
isScreenOn() {
# success if all outputs have power
swaymsg -t get_outputs --raw \
| jq --exit-status '. | all(.power)' \
> /dev/null
}
isAllOn() {
isTouchOn && isScreenOn
}
isInhibited() {
pidof rofi
}
ignore() {
true
}
inhibited() {
true
}
unmapped() {
true
}
allOn() {
swaymsg -- output '*' power true
swaymsg -- input type:touch events enabled
}
allOff() {
swaymsg -- output '*' power false
swaymsg -- input type:touch events disabled
}
toggleKeyboard() {
local kbpid=$(pidof "$KEYBOARD")
if [ -z "$kbpid" ] || ! ( env kill -s RTMIN+0 "$kbpid" ); then
echo "sane-input-handler: failed to toggle keyboard: $KEYBOARD"
fi
}
handleWith() {
state=
if [ -n "$_isInhibited" ]; then
state="inhibited+"
fi
if [ -n "$_isAllOn" ]; then
state="${state}on"
else
state="${state}off"
fi
echo "sane-input-handler: state=$state action=$action: handleWith: $@"
"$@"
exit 0
}
dispatchDefault() {
case "$action" in
"powerbutton_one")
# power once => unlock
handleWith allOn
;;
"powerbutton_two")
# power twice => screenoff
handleWith allOff
;;
# powerbutton_three: intentional no-op because overloading the kill-window handler is risky
volup_tap*|modal_volup_tap*)
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%+
;;
voldown_tap*|modal_voldown_tap*)
handleWith wpctl set-volume @DEFAULT_AUDIO_SINK@ "$VOL_INCR"%-
;;
volup_hold*|modal_volup_hold*)
handleWith playerctl position 30+
;;
voldown_hold*|modal_voldown_hold*)
handleWith playerctl position 10-
;;
esac
}
dispatchOff() {
case "$action" in
"powerbutton_two")
# power twice => toggle media player
handleWith playerctl play-pause
;;
"powerhold")
# power toggle during deep sleep often gets misread as power hold, so treat same
handleWith allOn
;;
esac
}
dispatchOn() {
case "$action" in
# powerbutton_one: intentional default to no-op
# powerbutton_two: intentional default to screenoff
"powerhold")
# power thrice: kill active window
# TODO: disable this if locked (with e.g. schlock, swaylock, etc)
handleWith swaymsg kill
;;
"powerbutton_volup")
# power (tap) -> volup: rotate CCW
handleWith swaymsg -- output '-' transform 90 anticlockwise
;;
"powerbutton_voldown")
# power (tap) -> voldown: rotate CW
handleWith swaymsg -- output '-' transform 90 clockwise
;;
"volup_tap_1")
# swallow: this could be the start to a media control (multi taps / holds),
# or it could be just a single tap -> release, handled next/below
handleWith ignore
;;
"volup_1")
# volume up once: system menu
handleWith sane-open-desktop rofi.desktop
;;
"voldown_start")
# volume down once: toggle keyboard
handleWith toggleKeyboard
;;
"voldown_hold_2")
# hold voldown to launch terminal
# note we already triggered the keyboard; that's fine: usually keyboard + terminal go together :)
# voldown_hold_1 frequently triggers during short taps meant only to reveal the keyboard,
# so prefer a longer hold duration
handleWith sane-open-desktop xdg-terminal-exec.desktop
;;
"voldown_tap_1")
# swallow, to prevent keyboard from also triggering media controls
handleWith ignore
;;
voldown_hold_*)
# swallow, to prevent terminal from also triggering media controls
handleWith ignore
;;
esac
}
dispatchInhibited() {
case "$action" in
"powerhold")
# power thrice: escape hatch in case rofi has hung
handleWith killall -9 rofi
;;
*)
# eat everything else (and let rofi consume it)
handleWith inhibited
;;
esac
}
_isAllOn="$(isAllOn && echo 1 || true)"
_isInhibited="$(isInhibited && echo 1 || true)"
if [ -n "$_isInhibited" ]; then
dispatchInhibited
fi
if [ -n "$_isAllOn" ]; then
dispatchOn
else
dispatchOff
fi
dispatchDefault
handleWith unmapped

View File

@@ -1,18 +0,0 @@
{ ... }:
{
sane.programs.sane-screenshot = {
sandbox.method = "bwrap";
sandbox.whitelistWayland = true;
sandbox.whitelistDbus = [ "user" ]; #< to send notifications
sandbox.extraHomePaths = [
"Pictures/Screenshots"
];
suggestedPrograms = [
"libnotify"
"swappy"
"sway-contrib.grimshot"
"util-linux"
"wl-clipboard"
];
};
}

View File

@@ -54,6 +54,7 @@ in
"sane-scripts.bt-add".sandbox = { "sane-scripts.bt-add".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
net = "clearnet"; net = "clearnet";
# TODO: migrate `transmission_passwd` to `secrets` api # TODO: migrate `transmission_passwd` to `secrets` api
extraPaths = [ "/run/secrets/transmission_passwd" ]; extraPaths = [ "/run/secrets/transmission_passwd" ];
@@ -61,6 +62,7 @@ in
"sane-scripts.bt-rm".sandbox = { "sane-scripts.bt-rm".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
net = "clearnet"; net = "clearnet";
# TODO: migrate `transmission_passwd` to `secrets` api # TODO: migrate `transmission_passwd` to `secrets` api
extraPaths = [ "/run/secrets/transmission_passwd" ]; extraPaths = [ "/run/secrets/transmission_passwd" ];
@@ -68,6 +70,7 @@ in
"sane-scripts.bt-search".sandbox = { "sane-scripts.bt-search".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
net = "clearnet"; net = "clearnet";
# TODO: migrate `jackett_apikey` to `secrets` api # TODO: migrate `jackett_apikey` to `secrets` api
extraPaths = [ "/run/secrets/jackett_apikey" ]; extraPaths = [ "/run/secrets/jackett_apikey" ];
@@ -75,6 +78,7 @@ in
"sane-scripts.bt-show".sandbox = { "sane-scripts.bt-show".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
net = "clearnet"; net = "clearnet";
# TODO: migrate `transmission_passwd` to `secrets` api # TODO: migrate `transmission_passwd` to `secrets` api
extraPaths = [ "/run/secrets/transmission_passwd" ]; extraPaths = [ "/run/secrets/transmission_passwd" ];
@@ -86,11 +90,13 @@ in
"sane-scripts.deadlines".sandbox = { "sane-scripts.deadlines".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraHomePaths = [ "knowledge/planner/deadlines.tsv" ]; extraHomePaths = [ "knowledge/planner/deadlines.tsv" ];
}; };
"sane-scripts.dev-cargo-loop".sandbox = { "sane-scripts.dev-cargo-loop".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
net = "clearnet"; net = "clearnet";
whitelistPwd = true; whitelistPwd = true;
extraPaths = [ extraPaths = [
@@ -104,6 +110,7 @@ in
"sane-scripts.find-dotfiles".sandbox = { "sane-scripts.find-dotfiles".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraHomePaths = [ extraHomePaths = [
"/" "/"
".persist/ephemeral" ".persist/ephemeral"
@@ -113,6 +120,7 @@ in
"sane-scripts.ip-check".sandbox = { "sane-scripts.ip-check".sandbox = {
method = "landlock"; method = "landlock";
wrapperType = "wrappedDerivation";
net = "all"; net = "all";
}; };
@@ -120,6 +128,7 @@ in
"sane-scripts.private-change-passwd".sandbox = { "sane-scripts.private-change-passwd".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
autodetectCliPaths = "existing"; #< for the new `private` location autodetectCliPaths = "existing"; #< for the new `private` location
capabilities = [ "sys_admin" ]; # it needs to mount the new store capabilities = [ "sys_admin" ]; # it needs to mount the new store
extraHomePaths = [ extraHomePaths = [
@@ -131,6 +140,7 @@ in
# instead, we put ourselves in a mount namespace, do the mount, and drop into a shell or run a command. # instead, we put ourselves in a mount namespace, do the mount, and drop into a shell or run a command.
# this actually has an OK side effect, that the mount isn't shared, and so we avoid contention/interleaving that would cause the ending `umount` to fail. # this actually has an OK side effect, that the mount isn't shared, and so we avoid contention/interleaving that would cause the ending `umount` to fail.
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
# cap_sys_admin is needed to mount stuff. # cap_sys_admin is needed to mount stuff.
# ordinarily /run/wrappers/bin/mount would do that via setuid, but sandboxes have no_new_privs by default. # ordinarily /run/wrappers/bin/mount would do that via setuid, but sandboxes have no_new_privs by default.
capabilities = [ "sys_admin" ]; capabilities = [ "sys_admin" ];
@@ -141,6 +151,7 @@ in
}; };
"sane-scripts.private-init".sandbox = { "sane-scripts.private-init".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
capabilities = [ "sys_admin" ]; # it needs to mount the new store capabilities = [ "sys_admin" ]; # it needs to mount the new store
extraHomePaths = [ extraHomePaths = [
".persist/private" ".persist/private"
@@ -151,6 +162,7 @@ in
"sane-scripts.reclaim-boot-space".sandbox = { "sane-scripts.reclaim-boot-space".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraPaths = [ "/boot" ]; extraPaths = [ "/boot" ];
}; };
@@ -161,6 +173,7 @@ in
"sane-scripts.reboot".sandbox = { "sane-scripts.reboot".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraPaths = [ extraPaths = [
"/run/dbus" "/run/dbus"
"/run/systemd" "/run/systemd"
@@ -169,11 +182,13 @@ in
"sane-scripts.reclaim-disk-space".sandbox = { "sane-scripts.reclaim-disk-space".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraPaths = [ "/nix/var/nix" ]; extraPaths = [ "/nix/var/nix" ];
}; };
"sane-scripts.secrets-unlock".sandbox = { "sane-scripts.secrets-unlock".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraHomePaths = [ extraHomePaths = [
".ssh/id_ed25519" ".ssh/id_ed25519"
".ssh/id_ed25519.pub" ".ssh/id_ed25519.pub"
@@ -199,6 +214,7 @@ in
"sane-scripts.shutdown".sandbox = { "sane-scripts.shutdown".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraPaths = [ extraPaths = [
"/run/dbus" "/run/dbus"
"/run/systemd" "/run/systemd"
@@ -215,6 +231,7 @@ in
"sane-scripts.tag-music".sandbox = { "sane-scripts.tag-music".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
autodetectCliPaths = "existing"; autodetectCliPaths = "existing";
}; };
@@ -239,6 +256,7 @@ in
(builtins.attrNames config.sane.vpn); (builtins.attrNames config.sane.vpn);
"sane-scripts.vpn".sandbox = { "sane-scripts.vpn".sandbox = {
method = "landlock"; #< bwrap can't handle `ip link` stuff even with cap_net_admin method = "landlock"; #< bwrap can't handle `ip link` stuff even with cap_net_admin
wrapperType = "wrappedDerivation";
net = "all"; net = "all";
capabilities = [ "net_admin" ]; capabilities = [ "net_admin" ];
extraHomePaths = [ ".config/sane-vpn" ]; extraHomePaths = [ ".config/sane-vpn" ];
@@ -246,6 +264,7 @@ in
"sane-scripts.which".sandbox = { "sane-scripts.which".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
extraHomePaths = [ extraHomePaths = [
# for SXMO # for SXMO
".config/sxmo/hooks" ".config/sxmo/hooks"
@@ -254,6 +273,7 @@ in
"sane-scripts.wipe".sandbox = { "sane-scripts.wipe".sandbox = {
method = "bwrap"; method = "bwrap";
wrapperType = "wrappedDerivation";
whitelistDbus = [ "user" ]; #< for `secret-tool` and `systemd --user stop <service> whitelistDbus = [ "user" ]; #< for `secret-tool` and `systemd --user stop <service>
extraHomePaths = [ extraHomePaths = [
# could be more specific, but at a maintenance cost. # could be more specific, but at a maintenance cost.
@@ -264,21 +284,6 @@ in
".local/share" ".local/share"
".librewolf" ".librewolf"
".mozilla" ".mozilla"
".persist/ephemeral/.cache"
".persist/ephemeral/.config"
".persist/ephemeral/.local/share"
".persist/ephemeral/.librewolf"
".persist/ephemeral/.mozilla"
".persist/plaintext/.cache"
".persist/plaintext/.config"
".persist/plaintext/.local/share"
".persist/plaintext/.librewolf"
".persist/plaintext/.mozilla"
".persist/private/.cache"
".persist/private/.config"
".persist/private/.local/share"
".persist/private/.librewolf"
".persist/private/.mozilla"
]; ];
}; };
}; };

View File

@@ -1,24 +0,0 @@
# limitations:
# - schlock fails open (pkill it and the wayland session is left unprotected)
# - schlock does not accept keyboard input; hence, unusable without a touchscreen
# - pin is not synchronized with PAM.
# - generate a hashed pin with: `mkpin`
# - does not seem to render in landscape mode
{ config, lib, ... }:
let
cfg = config.sane.programs.schlock;
in
{
sane.programs.schlock = {
sandbox.method = "bwrap";
sandbox.whitelistWayland = true;
secrets.".config/schlock/schlock.pin" = ../../../secrets/common/schlock.pin.bin;
};
sane.programs.swayidle.config = lib.mkIf cfg.enabled {
actions.schlock.desktop = "schlock.desktop";
actions.schlock.delay = 1800;
};
}

Some files were not shown because too many files have changed in this diff Show More