Compare commits

..

3 Commits

354 changed files with 3709 additions and 17047 deletions

View File

@@ -8,7 +8,7 @@ keys:
- &host_servo age1tzlyex2z6t88tg9h82943e39shxhmqeyr7ywhlwpdjmyqsndv3qq27x0rf
- &host_moby age18vq5ktwgeaysucvw9t67drqmg5zd5c5k3le34yqxckkfj7wqdqgsd4ejmt
creation_rules:
- path_regex: secrets/common*
- path_regex: secrets/universal*
key_groups:
- age:
- *user_desko_colin
@@ -26,19 +26,19 @@ creation_rules:
- *user_lappy_colin
- *user_servo_colin
- *host_servo
- path_regex: secrets/desko*
- path_regex: secrets/desko.yaml$
key_groups:
- age:
- *user_desko_colin
- *user_lappy_colin
- *host_desko
- path_regex: secrets/lappy*
- path_regex: secrets/lappy.yaml$
key_groups:
- age:
- *user_lappy_colin
- *user_desko_colin
- *host_lappy
- path_regex: secrets/moby*
- path_regex: secrets/moby.yaml$
key_groups:
- age:
- *user_desko_colin

108
README.md
View File

@@ -1,108 +0,0 @@
## What's Here
this is the top-level repo from which i configure/deploy all my NixOS machines:
- desktop
- laptop
- server
- mobile phone
i enjoy a monorepo approach. this repo references [nixpkgs][nixpkgs], a couple 3rd party
nix modules like [sops][sops], the sources for [uninsane.org][uninsane-org], and that's
about it. custom derivations and modules (some of which i try to upstream) live
directly here; even the sources for those packages is often kept here too.
[nixpkgs]: https://github.com/NixOS/nixpkgs
[sops]: https://github.com/Mic92/sops-nix
[uninsane-org]: https://uninsane.org
## Layout
- `hosts/`
- the bulk of config which isn't factored with external use in mind.
- that is, if you were to add this repo to a flake.nix for your own use,
you won't likely be depending on anything in this directory.
- `integrations/`
- code intended for consumption by external tools (e.g. the Nix User Repos)
- `modules/`
- config which is gated behind `enable` flags, in similar style to nixpkgs'
`nixos/` directory.
- if you depend on this repo, it's most likely for something in this directory.
- `nixpatches/`
- literally, diffs i apply atop upstream nixpkgs before performing further eval.
- `overlays/`
- exposed via the `overlays` output in `flake.nix`.
- predominantly a list of `callPackage` directives.
- `pkgs/`
- derivations for things not yet packaged in nixpkgs.
- derivations for things from nixpkgs which i need to `override` for some reason.
- inline code for wholly custom packages (e.g. `pkgs/additional/sane-scripts/` for CLI tools
that are highly specific to my setup).
- `scripts/`
- scripts which are referenced by other things in this repo.
- these aren't generally user-facing, but they're factored out so that they can
be invoked directly when i need to debug.
- `secrets/`
- encrypted keys, API tokens, anything which one or more of my machines needs
read access to but shouldn't be world-readable.
- not much to see here
- `templates/`
- exposed via the `templates` output in `flake.nix`.
- used to instantiate short-lived environments.
- used to auto-fill the boiler-plate portions of new packages.
## Key Points of Interest
i.e. you might find value in using these in your own config:
- `modules/fs/`
- use this to statically define leafs and nodes anywhere in the filesystem,
not just inside `/nix/store`.
- e.g. specify that `/var/www` should be:
- owned by a specific user/group
- set to a specific mode
- symlinked to some other path
- populated with some statically-defined data
- populated according to some script
- created as a dependency of some service (e.g. `nginx`)
- values defined here are applied neither at evaluation time _nor_ at activation time.
- rather, they become systemd services.
- systemd manages dependencies
- e.g. link `/var/www -> /mnt/my-drive/www` only _after_ `/mnt/my-drive/www` appears)
- this is akin to using [Home Manager's][home-manager] file API -- the part which lets you
statically define `~/.config` files -- just with a different philosophy.
- `modules/persist/`
- my alternative to the Impermanence module.
- this builds atop `modules/fs/` to achieve things stock impermanence can't:
- persist things to encrypted storage which is unlocked at login time (pam_mount).
- "persist" cache directories -- to free up RAM -- but auto-wipe them on mount
and encrypt them to ephemeral keys so they're unreadable post shutdown/unmount.
- `modules/programs.nix`
- like nixpkgs' `programs` options, but allows both system-wide or per-user deployment.
- allows `fs` and `persist` config values to be gated behind program deployment:
- e.g. `/home/<user>/.mozilla/firefox` is persisted only for users who
`sane.programs.firefox.enableFor.user."<user>" = true;`
- `modules/users.nix`
- convenience layer atop the above modules so that you can just write
`fs.".config/git"` instead of `fs."/home/colin/.config/git"`
some things in here could easily find broader use. if you would find benefit in
them being factored out of my config, message me and we could work to make that happen.
[home-manager]: https://github.com/nix-community/home-manager
## Using This Repo In Your Own Config
this should be a pretty "standard" flake. just reference it, and import either
- `nixosModules.sane` (for the modules)
- `overlays.pkgs` (for the packages)
## Mirrors
this repo exists in a few known locations:
- primary: <https://git.uninsane.org/colin/nix-files>
- mirror: <https://github.com/nix-community/nur-combined/tree/master/repos/colinsane>
## Contact
if you want to contact me for questions, or collaborate to split something useful into a shared repo, etc,
you can reach me via any method listed [here](https://uninsane.org/about).

75
TODO.md
View File

@@ -1,75 +0,0 @@
## BUGS
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
- else DNS fails
## REFACTORING:
### sops/secrets
- attach secrets to the thing they're used by (sane.programs)
- rework secrets to leverage `sane.fs`
- remove sops activation script as it's covered by my systemd sane.fs impl
### roles
- allow any host to take the role of `uninsane.org`
- will make it easier to test new services?
### upstreaming
- split out a trust-dns module
- see: <https://github.com/NixOS/nixpkgs/pull/205866#issuecomment-1575753054>
- bump nodejs version in lemmy-ui
- add updateScripts to all my packages in nixpkgs
- fix lightdm-mobile-greeter for newer libhandy
- port zecwallet-lite to a from-source build
- fix or abandon Whalebird
## IMPROVEMENTS:
### security/resilience
- validate duplicity backups!
- encrypt more ~ dirs (~/archives, ~/records, ..?)
- best to do this after i know for sure i have good backups
- have `sane.programs` be wrapped such that they run in a cgroup?
- at least, only give them access to the portion of the fs they *need*.
- Android takes approach of giving each app its own user: could hack that in here.
- canaries for important services
- e.g. daily email checks; daily backup checks
### user experience
- firefox/librewolf: don't show browserpass/sponsorblock/metamask "first run" on every boot
- moby: improve gPodder launch time
- moby: replace jellyfin-desktop with jellyfin-vue?
- allows (maybe) to cache media for offline use
- "newer" jellyfin client
- not packaged for nix
- find a nice desktop ActivityPub client
- package Nix/NixOS docs for Zeal
- install [doc-browser](https://github.com/qwfy/doc-browser)
- this supports both dash (zeal) *and* the datasets from <https://devdocs.io> (which includes nix!)
- install [devhelp](https://wiki.gnome.org/Apps/Devhelp) (gnome)
- auto-mount servo
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
- `sane.programs`: auto-populate defaults with everything from `pkgs`
- zsh: disable "command not found" corrections
- sxmo: allow rotation to the upside-down position
- see: <repo:mil/sxmo-utils:scripts/core/sxmo_autorotate.sh>
- all orientations *except* upside down are supported
- sxmo: launch with auto-rotation enabled
### perf
- why does nixos-rebuild switch take 5 minutes when net is flakey?
- trying to auto-mount servo?
- something to do with systemd services restarting/stalling
- maybe wireguard & its refresh operation, specifically?
- fix OOM for large builds like webkitgtk
- these use significant /tmp space.
- either place /tmp on encrypted-cleared-at-boot storage
- which probably causes each CPU load for the encryption
- **or set up encrypted swap**
- encrypted swap could remove the need for my encrypted-cleared-at-boot stuff
## NEW FEATURES:
- add a FTP-accessible file share to servo
- just /var/www?
- migrate MAME cabinet to nix
- boot it from PXE from servo?
- enable IPv6

82
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1678901627,
"narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@@ -18,11 +18,11 @@
"mobile-nixos": {
"flake": false,
"locked": {
"lastModified": 1683422260,
"narHash": "sha256-79zaClbubRkBNlJ04OSADILuLQHH48N5fu296hEWYlw=",
"lastModified": 1674880620,
"narHash": "sha256-JMALuC7xcoH/T66sKTVLuItHfOJBCWsNKpE49Qrvs80=",
"owner": "nixos",
"repo": "mobile-nixos",
"rev": "ba4638836e94a8f16d1d1f9e8c0530b86078029c",
"rev": "7478a9ffad737486951186b66f6c5535dc5802e2",
"type": "github"
},
"original": {
@@ -31,46 +31,30 @@
"type": "github"
}
},
"nix-serve": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1684319086,
"narHash": "sha256-5wwlkWqP1cQUPXp/PJsi09FkgAule5yBghngRZZbUQg=",
"owner": "edolstra",
"repo": "nix-serve",
"rev": "e6e3d09438e803daa5374ad8edf1271289348456",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "nix-serve",
"type": "github"
}
},
"nixpkgs": {
"inputs": {
"nixpkgs": [
"nixpkgs-unpatched"
]
},
"locked": {
"lastModified": 1606086654,
"narHash": "sha256-VFl+3eGIMqNp7cyOMJ6TjM/+UcsLKtodKoYexrlTJMI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "19db3e5ea2777daa874563b5986288151f502e27",
"type": "github"
"lastModified": 1,
"narHash": "sha256-rkVbviFmYYmbbVfvFRtOM95IjETbNu3I517Hrxp8EF4=",
"path": "/nix/store/8azr0ivnzf0y1sh2r7alxaxab3w49ggx-source/nixpatches",
"type": "path"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-20.09",
"type": "indirect"
"path": "/nix/store/8azr0ivnzf0y1sh2r7alxaxab3w49ggx-source/nixpatches",
"type": "path"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1684632198,
"narHash": "sha256-SdxMPd0WmU9MnDBuuy7ouR++GftrThmSGL7PCQj/uVI=",
"lastModified": 1675265860,
"narHash": "sha256-PZNqc4ZnTRT34NsHJYbXn+Yhghh56l8HEXn39SMpGNc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d0dade110dc7072d67ce27826cfe9ab2ab0cf247",
"rev": "a3a1400571e3b9ccc270c2e8d36194cf05aab6ce",
"type": "github"
},
"original": {
@@ -82,11 +66,11 @@
},
"nixpkgs-unpatched": {
"locked": {
"lastModified": 1684935479,
"narHash": "sha256-6QMMsXMr2nhmOPHdti2j3KRHt+bai2zw+LJfdCl97Mk=",
"lastModified": 1675273418,
"narHash": "sha256-tpYc4TEGvDzh9uRf44QemyQ4TpVuUbxb07b2P99XDbM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "f91ee3065de91a3531329a674a45ddcb3467a650",
"rev": "4d7c2644dbac9cf8282c0afe68fca8f0f3e7b2db",
"type": "github"
},
"original": {
@@ -99,7 +83,7 @@
"root": {
"inputs": {
"mobile-nixos": "mobile-nixos",
"nix-serve": "nix-serve",
"nixpkgs": "nixpkgs",
"nixpkgs-unpatched": "nixpkgs-unpatched",
"sops-nix": "sops-nix",
"uninsane-dot-org": "uninsane-dot-org"
@@ -108,16 +92,16 @@
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs-unpatched"
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1684637723,
"narHash": "sha256-0vAxL7MVMhGbTkAyvzLvleELHjVsaS43p+PR1h9gzNQ=",
"lastModified": 1675288837,
"narHash": "sha256-76s8TLENa4PzWDeuIpEF78gqeUrXi6rEJJaKEAaJsXw=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "4ccdfb573f323a108a44c13bb7730e42baf962a9",
"rev": "a81ce6c961480b3b93498507074000c589bd9d60",
"type": "github"
},
"original": {
@@ -130,15 +114,15 @@
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unpatched"
"nixpkgs"
]
},
"locked": {
"lastModified": 1684528780,
"narHash": "sha256-QdYxjcTCCLPv++1v9tJBL98nn/AFx0fmzlgzcLK6KRE=",
"lastModified": 1675131883,
"narHash": "sha256-yBgJDG72YqIr1bltasqHD1E/kHc9uRFgDjxDmy6kI8M=",
"ref": "refs/heads/master",
"rev": "f3747a1dad3d34880613821faf26357ba432d3d7",
"revCount": 194,
"rev": "b099c24091cc192abf3997b94342d4b31cc5757b",
"revCount": 170,
"type": "git",
"url": "https://git.uninsane.org/colin/uninsane"
},

257
flake.nix
View File

@@ -12,11 +12,6 @@
# - Flake RFC: <https://github.com/tweag/rfcs/blob/flakes/rfcs/0049-flakes.md>
# - Discussion: <https://github.com/NixOS/rfcs/pull/49>
# - <https://serokell.io/blog/practical-nix-flakes>
#
#
# COMMON OPERATIONS:
# - update a specific flake input:
# - `nix flake lock --update-input nixpkgs`
{
# XXX: use the `github:` scheme instead of the more readable git+https: because it's *way* more efficient
@@ -26,27 +21,12 @@
# <https://github.com/nixos/nixpkgs/tree/nixos-22.11>
# nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
# branch workflow:
# - daily:
# - nixos-unstable cut from master after enough packages have been built in caches.
# - every 6 hours:
# - master auto-merged into staging.
# - staging-next auto-merged into staging.
# - manually, approximately once per month:
# - staging-next is cut from staging.
# - staging-next merged into master.
#
# which branch to source from?
# - for everyday development, prefer `nixos-unstable` branch, as it provides good caching.
# - if need to test bleeding updates (e.g. if submitting code into staging):
# - use `staging-next` if it's been cut (i.e. if there's an active staging-next -> master PR)
# - use `staging` if no staging-next branch has been cut.
#
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=staging-next";
# nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=staging";
nixpkgs = {
url = "./nixpatches";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
};
mobile-nixos = {
# <https://github.com/nixos/mobile-nixos>
url = "github:nixos/mobile-nixos";
@@ -55,99 +35,68 @@
sops-nix = {
# <https://github.com/Mic92/sops-nix>
url = "github:Mic92/sops-nix";
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
inputs.nixpkgs.follows = "nixpkgs";
};
uninsane-dot-org = {
url = "git+https://git.uninsane.org/colin/uninsane";
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
};
nix-serve = {
# <https://github.com/edolstra/nix-serve>
url = "github:edolstra/nix-serve";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
nixpkgs-unpatched,
mobile-nixos,
sops-nix,
uninsane-dot-org,
nix-serve,
...
}@inputs:
let
inherit (builtins) attrNames elem listToAttrs map mapAttrs;
mapAttrs' = f: set:
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
# mapAttrs but without the `name` argument
mapAttrValues = f: mapAttrs (_: f);
# rather than apply our nixpkgs patches as a flake input, do that here instead.
# this (temporarily?) resolves the bad UX wherein a subflake residing in the same git
# repo as the main flake causes the main flake to have an unstable hash.
nixpkgs = (import ./nixpatches/flake.nix).outputs {
self = nixpkgs;
nixpkgs = nixpkgs-unpatched;
};
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
nixpkgsCompiledBy = system: nixpkgs.legacyPackages."${system}";
evalHost = { name, local, target }: nixpkgs.lib.nixosSystem {
system = target;
modules = [
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
self.nixosModules.default
self.nixosModules.passthru
{
nixpkgs.overlays = [
self.overlays.passthru
self.overlays.sane-all
];
}
({ lib, ... }: {
# TODO: does the earlier `system` arg to nixosSystem make its way here?
nixpkgs.hostPlatform.system = target;
# nixpkgs.buildPlatform = local; # set by instantiate.nix instead
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
})
];
};
in {
nixosConfigurations =
evalHost = { name, local, target }:
let
hosts = {
servo = { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
desko = { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
lappy = { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
moby = { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
rescue = { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
};
# cross-compiled builds: instead of emulating the host, build using a cross-compiler.
# - these are faster to *build* than the emulated variants (useful when tweaking packages),
# - but fewer of their packages can be found in upstream caches.
cross = mapAttrValues evalHost hosts;
emulated = mapAttrValues
({name, local, target}: evalHost {
inherit name target;
local = null;
})
hosts;
prefixAttrs = prefix: attrs: mapAttrs'
(name: value: {
name = prefix + name;
inherit value;
})
attrs;
# XXX: we'd prefer to use `nixosSystem = (nixpkgsCompiledBy target).nixos`
# but it doesn't propagate config to the underlying pkgs, meaning it doesn't let you use
# non-free packages even after setting nixpkgs.allowUnfree.
# XXX: patch using the target -- not local -- otherwise the target will
# need to emulate the host in order to rebuild!
nixosSystem = import ((nixpkgsCompiledBy target).path + "/nixos/lib/eval-config.nix");
in
(prefixAttrs "cross-" cross) //
(prefixAttrs "emulated-" emulated) // {
# prefer native builds for these machines:
inherit (emulated) servo desko lappy rescue;
# prefer cross-compiled builds for these machines:
inherit (cross) moby;
};
(nixosSystem {
# we use pkgs built for and *by* the target, i.e. emulation, by default.
# cross compilation only happens on explicit access to `pkgs.cross`
system = target;
modules = [
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
self.nixosModules.default
self.nixosModules.passthru
{
nixpkgs.overlays = [
self.overlays.default
self.overlays.passthru
self.overlays.pins
];
# nixpkgs.crossSystem = target;
nixpkgs.hostPlatform = target;
nixpkgs.buildPlatform = local;
}
];
});
in {
nixosConfigurations = {
servo = evalHost { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
desko = evalHost { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
lappy = evalHost { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
moby = evalHost { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
# v.s. emulate differ.
# so deploying foo-cross and then foo incurs some rebuilding.
moby-cross = evalHost { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
rescue = evalHost { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
};
# unofficial output
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
@@ -163,44 +112,25 @@
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
# - `nixos-rebuild --flake './#<host>' switch`
imgs = mapAttrValues (host: host.config.system.build.img) self.nixosConfigurations;
imgs = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.img) self.nixosConfigurations;
# unofficial output
host-pkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
host-programs = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
overlays = {
# N.B.: `nix flake check` requires every overlay to take `final: prev:` at defn site,
# hence the weird redundancy.
default = final: prev: self.overlays.pkgs final prev;
sane-all = final: prev: import ./overlays/all.nix final prev;
disable-flakey-tests = final: prev: import ./overlays/disable-flakey-tests.nix final prev;
pkgs = final: prev: import ./overlays/pkgs.nix final prev;
pins = final: prev: import ./overlays/pins.nix final prev;
optimizations = final: prev: import ./overlays/optimizations.nix final prev;
passthru = final: prev:
overlays = rec {
default = pkgs;
pkgs = import ./overlays/pkgs.nix;
pins = import ./overlays/pins.nix; # TODO: move to `nixpatches/` input
passthru =
let
stable =
if inputs ? "nixpkgs-stable" then (
final': prev': {
stable = inputs.nixpkgs-stable.legacyPackages."${prev'.stdenv.hostPlatform.system}";
next: prev: {
stable = inputs.nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
}
) else (final': prev': {});
) else (next: prev: {});
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
uninsane = uninsane-dot-org.overlay;
# nix-serve' = nix-serve.overlay;
nix-serve' = next: prev: {
# XXX(2023/03/02): upstream isn't compatible with modern `nix`. probably the perl bindings.
# - we use the package built against `nixpkgs` specified in its flake rather than use its overlay,
# to get around this.
inherit (nix-serve.packages."${next.system}") nix-serve;
};
in
(stable final prev)
// (mobile final prev)
// (uninsane final prev)
// (nix-serve' final prev)
;
next: prev:
(stable next prev) // (mobile next prev) // (uninsane next prev);
};
nixosModules = rec {
@@ -224,87 +154,36 @@
aarch64-linux = allPkgsFor "aarch64-linux";
};
# extract only our own packages from the full set.
# because of `nix flake check`, we flatten the package set and only surface x86_64-linux packages.
packages = mapAttrs
(system: allPkgs:
allPkgs.lib.filterAttrs (name: pkg:
# keep only packages which will pass `nix flake check`, i.e. keep only:
# - derivations (not package sets)
# - packages that build for the given platform
(! elem name [ "feeds" "pythonPackagesExtensions" ])
&& (allPkgs.lib.meta.availableOn allPkgs.stdenv.hostPlatform pkg)
)
(
# expose sane packages and chosen inputs (uninsane.org)
(import ./pkgs { pkgs = allPkgs; }) // {
inherit (allPkgs) uninsane-dot-org;
}
)
)
# self.legacyPackages;
{ inherit (self.legacyPackages) x86_64-linux; }
;
# extract only our own packages from the full set
packages = builtins.mapAttrs
(_: full: full.sane // { inherit (full) sane uninsane-dot-org; })
self.legacyPackages;
apps."x86_64-linux" =
let
pkgs = self.legacyPackages."x86_64-linux";
deployScript = action: pkgs.writeShellScript "deploy-moby" ''
nixos-rebuild --flake '.#moby' build $@
sudo nix sign-paths -r -k /run/secrets/nix_serve_privkey $(readlink ./result)
nixos-rebuild --flake '.#moby' ${action} --target-host colin@moby-hn --use-remote-sudo $@
'';
in {
update-feeds = {
type = "app";
program = "${pkgs.feeds.updateScript}";
program = "${pkgs.feeds.passthru.updateScript}";
};
init-feed = {
# use like `nix run '.#init-feed' uninsane.org`
type = "app";
program = "${pkgs.feeds.initFeedScript}";
};
deploy-moby-test = {
# `nix run '.#deploy-moby-test'`
type = "app";
program = ''${deployScript "test"}'';
};
deploy-moby = {
# `nix run '.#deploy-moby-switch'`
type = "app";
program = ''${deployScript "switch"}'';
program = "${pkgs.feeds.passthru.initFeedScript}";
};
};
templates = {
env.python-data = {
python-data = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#env.python-data'`
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
# then enter with:
# - `nix develop`
path = ./templates/env/python-data;
path = ./templates/python-data;
description = "python environment for data processing";
};
pkgs.rust-inline = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.rust-inline'`
path = ./templates/pkgs/rust-inline;
description = "rust package and development environment (inline rust sources)";
};
pkgs.rust = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.rust'`
path = ./templates/pkgs/rust;
description = "rust package fit to ship in nixpkgs";
};
pkgs.make = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#pkgs.make'`
path = ./templates/pkgs/make;
description = "default Makefile-based derivation";
};
};
};
}

View File

@@ -1,7 +0,0 @@
## directory structure
- by-name/<hostname>: configuration which is evaluated _only_ for the given hostname
- common/: configuration which applies to all hosts
- modules/: nixpkgs-style modules which may be used by multiple hosts, but configured separately per host.
- ideally no module here has effect unless `enable`d
- however, `enable` may default to true
- and in practice some of these modules surely aren't fully "disableable"

View File

@@ -4,21 +4,15 @@
./fs.nix
];
sops.secrets.colin-passwd.neededForUsers = true;
sane.roles.build-machine.enable = true;
sane.roles.ac = true;
sane.roles.client = true;
sane.roles.dev-machine = true;
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
sane.services.duplicity.enable = true;
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
sane.services.nixserve.enable = true;
sane.services.nixserve.sopsFile = ../../../secrets/desko.yaml;
sane.persist.enable = true;
sane.gui.sway.enable = true;
sane.programs.iphoneUtils.enableFor.user.colin = true;
sane.programs.guiApps.suggestedPrograms = [ "desktopGuiApps" ];
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
@@ -26,6 +20,11 @@
# needed to use libimobiledevice/ifuse, for iphone sync
services.usbmuxd.enable = true;
sops.secrets.colin-passwd = {
sopsFile = ../../../secrets/desko.yaml;
neededForUsers = true;
};
# don't enable wifi by default: it messes with connectivity.
systemd.services.iwd.enable = false;
@@ -36,9 +35,15 @@
services.snapper.configs.nix = {
# TODO: for the impermanent setup, we'd prefer to just do /nix/persist,
# but that also requires setting up the persist dir as a subvol
SUBVOLUME = "/nix";
subvolume = "/nix";
# TODO: ALLOW_USERS doesn't seem to work. still need `sudo snapper -c nix list`
ALLOW_USERS = [ "colin" ];
extraConfig = ''
ALLOW_USERS = "colin";
'';
};
sops.secrets.duplicity_passphrase = {
sopsFile = ../../../secrets/desko.yaml;
};
programs.steam = {

View File

@@ -2,25 +2,23 @@
{
imports = [
./fs.nix
./polyfill.nix
];
sane.roles.client = true;
sane.roles.dev-machine = true;
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
# sane.guest.enable = true;
sane.gui.sway.enable = true;
sane.persist.enable = true;
sane.nixcache.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.programs.guiApps.suggestedPrograms = [
"desktopGuiApps"
"stepmania"
];
sops.secrets.colin-passwd.neededForUsers = true;
sops.secrets.colin-passwd = {
sopsFile = ../../../secrets/lappy.yaml;
neededForUsers = true;
};
# default config: https://man.archlinux.org/man/snapper-configs.5
# defaults to something like:
@@ -29,8 +27,7 @@
services.snapper.configs.nix = {
# TODO: for the impermanent setup, we'd prefer to just do /nix/persist,
# but that also requires setting up the persist dir as a subvol
SUBVOLUME = "/nix";
ALLOW_USERS = [ "colin" ];
subvolume = "/nix";
};
# TODO: only here for debugging

View File

@@ -1,32 +0,0 @@
# doesn't actually *enable* anything,
# but sets up any modules such that if they *were* enabled, they'll act as expected.
{ ... }:
{
sane.gui.sxmo = {
greeter = "sway";
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";
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_SYS_FILES
# - 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";
};
};
}

View File

@@ -4,21 +4,33 @@
./firmware.nix
./fs.nix
./kernel.nix
./polyfill.nix
];
sane.roles.client = true;
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
# TODO: re-enable once base is cross-compiled
sane.programs.phoshApps.enableSuggested = false;
# cross-compiled documentation is *slow*.
# no obvious way to natively compile docs (2022/09/29).
# entrypoint is nixos/modules/misc/documentation.nix
# doc building happens in nixos/doc/manual/default.nix
# TODO: we could *maybe* inject pkgs.buildPackages.xyz = cross.buildPackages.xyz?
documentation.nixos.enable = false;
# XXX colin: phosh doesn't work well with passwordless login,
# so set this more reliable default password should anything go wrong
users.users.colin.initialPassword = "147147";
services.getty.autologinUser = "root"; # allows for emergency maintenance?
sops.secrets.colin-passwd.neededForUsers = true;
sops.secrets.colin-passwd = {
sopsFile = ../../../secrets/moby.yaml;
neededForUsers = true;
};
sane.programs.web-browser.config = {
sane.web-browser = {
# compromise impermanence for the sake of usability
persistCache = "private";
persistData = "private";
@@ -29,15 +41,19 @@
};
sane.user.persist.plaintext = [
# TODO: make this just generally conditional upon pulse being enabled?
".config/pulse" # persist pulseaudio volume
];
sane.gui.phosh.enable = true;
# sane.programs.consoleUtils.enableFor.user.colin = false;
# sane.programs.guiApps.enableFor.user.colin = false;
sane.programs.sequoia.enableFor.user.colin = false;
sane.programs.tuiApps.enableFor.user.colin = false; # visidata, others, don't compile well
sane.programs."pkgs.plasma5Packages.konsole" = {
# more reliable terminal
# TODO: move to gui/phosh
package = pkgs.plasma5Packages.konsole;
enableFor.user.colin = true;
};
sane.nixcache.enable = true;
sane.persist.enable = true;
# sane.gui.phosh.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
# /boot space is at a premium. default was 20.
@@ -77,30 +93,14 @@
# enable rotation sensor
hardware.sensor.iio.enable = true;
# inject specialized alsa configs via the environment.
# specifically, this gets the pinephone headphones & internal earpiece working.
# see pkgs/patched/alsa-ucm-conf for more info.
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
environment.pathsToLink = [ "/share/alsa/ucm2" ];
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
systemd =
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
in {
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
# pulseaudio
user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
# 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;
};
# from https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone
# mobile-nixos does this same thing, with *slightly different settings*.
# i trust manjaro more because the guy maintaining that is actively trying to upstream into alsa-ucm-conf.
# an alternative may be to build a custom alsa with the PinePhone config patch applied:
# - <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
# that would make this be not device-specific
environment.variables.ALSA_CONFIG_UCM2 = "${./ucm2}";
systemd.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = "${./ucm2}";
hardware.opengl.driSupport = true;
}

View File

@@ -44,6 +44,68 @@ let
"sha256-6ywm3dQQ5JYl60CLKarxlSUukwi4QzqctCj3tVgzFbo="
)
];
# pinephone uses the linux dtb at arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
# - this includes sun50i-a64.dtsi
# - and sun50i-a64-cpu-opp.dtsi
# - no need to touch the allwinner-h6 stuff: that's the SBC pine product
# - i think it's safe to ignore sun9i stuff, but i don't know what it is
kernelConfig = with lib.kernel; {
# NB: nix adds the CONFIG_ prefix to each of these.
# if you add the prefix yourself nix will IGNORE YOUR CONFIG.
RTL8723CS = module;
BT_HCIUART_3WIRE = yes;
BT_HCIUART_RTL = yes;
RTL8XXXU_UNTESTED = yes;
BT_BNEP_MC_FILTER = yes;
BT_BNEP_PROTO_FILTER = yes;
BT_HS = yes;
BT_LE = yes;
# relevant configs inherited from nixos defaults (or above additions):
# CONFIG_BT=m
# CONFIG_BT_BREDR=y
# CONFIG_BT_RFCOMM=m
# CONFIG_BT_RFCOMM_TTY=y
# CONFIG_BT_BNEP=m
# CONFIG_BT_HIDP=m
# CONFIG_BT_RTL=m
# CONFIG_BT_HCIBTUSB=m
# CONFIG_BT_HCIBTUSB_BCM=y
# CONFIG_BT_HCIBTUSB_RTL=y
# CONFIG_BT_HCIUART=m
# CONFIG_BT_HCIUART_SERDEV=y
# CONFIG_BT_HCIUART_H4=y
# CONFIG_BT_HCIUART_LL=y
# CONFIG_RTL_CARDS=m
# CONFIG_RTLWIFI=m
# CONFIG_RTLWIFI_PCI=m
# CONFIG_RTLWIFI_USB=m
# CONFIG_RTLWIFI_DEBUG=y
# CONFIG_RTL8723_COMMON=m
# CONFIG_RTLBTCOEXIST=m
# CONFIG_RTL8XXXU=m
# CONFIG_RTLLIB=m
# consider adding (from mobile-nixos):
# maybe: CONFIG_BT_HCIUART_3WIRE=y
# maybe: CONFIG_BT_HCIUART_RTL=y
# maybe: CONFIG_RTL8XXXU_UNTESTED=y
# consider adding (from manjaro):
# CONFIG_BT_6LOWPAN=m (not listed as option in nixos kernel)
# these are referenced in the rtl8723 source, but not known to config (and not in mobile-nixos config
# maybe: CONFIG_RTL_ODM_WLAN_DRIVER
# maybe: CONFIG_RTL_TRIBAND_SUPPORT
# maybe: CONFIG_SDIO_HCI
# maybe: CONFIG_USB_HCI
};
# create a kernelPatch which overrides nixos' defconfig with extra options
patchDefconfig = config: {
# defconfig options. this method comes from here:
# - https://discourse.nixos.org/t/the-correct-way-to-override-the-latest-kernel-config/533/9
name = "sane-moby-defconfig";
patch = null;
extraStructuredConfig = config;
};
in
{
# use Megi's kernel:
@@ -52,7 +114,23 @@ in
# - phone rotation sensor is off by 90 degrees
# - ambient light sensor causes screen brightness to be shakey
# - phosh greeter may not appear after wake from sleep
boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-megous;
boot.kernelPackages = pkgs.cross.linuxPackagesFor pkgs.cross.linux-megous;
boot.kernelPatches = [
(patchDefconfig (kernelConfig //
(with lib.kernel; {
# disabling the sun5i_eink driver avoids this compilation error:
# CC [M] drivers/video/fbdev/sun5i-eink-neon.o
# aarch64-unknown-linux-gnu-gcc: error: unrecognized command line option '-mfloat-abi=softfp'
# aarch64-unknown-linux-gnu-gcc: error: unrecognized command line option '-mfpu=neon'
# make[3]: *** [../scripts/Makefile.build:289: drivers/video/fbdev/sun5i-eink-neon.o] Error 1
FB_SUN5I_EINK = no;
# used by the pinephone pro, but fails to compile with:
# ../drivers/media/i2c/ov8858.c:1834:27: error: implicit declaration of function 'compat_ptr'
VIDEO_OV8858 = no;
})
))
];
# alternatively, use nixos' kernel and add the stuff we want:
# # cross-compilation optimization:
@@ -65,19 +143,4 @@ in
# boot.kernelPatches = manjaroPatches ++ [
# (patchDefconfig kernelConfig)
# ];
nixpkgs.hostPlatform.linux-kernel = {
# defaults:
name = "aarch64-multiplatform";
baseConfig = "defconfig";
DTB = true;
autoModules = true;
preferBuiltin = true;
# extraConfig = ...
# ^-- raspberry pi stuff: we don't need it.
# target = "Image"; # <-- default
target = "Image.gz"; # <-- compress the kernel image
# target = "zImage"; # <-- confuses other parts of nixos :-(
};
}

View File

@@ -1,23 +0,0 @@
{ sane-lib, ... }:
{
sane.gui.sxmo = {
settings = {
# touch screen
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
# vol and power are detected correctly by upstream
};
};
# TODO: only populate this if sxmo is enabled?
sane.user.fs.".config/sxmo/profile" = sane-lib.fs.wantedText ''
# sourced by sxmo_init.sh
. sxmo_common.sh
export SXMO_SWAY_SCALE=1.5
export SXMO_ROTATION_GRAVITY=12800
export DEFAULT_COUNTRY=US
export BROWSER=librewolf
export SXMO_BG_IMG="$(xdg_data_path sxmo/background.jpg)"
'';
}

View File

@@ -1,4 +1,4 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
imports = [
./fs.nix
@@ -7,8 +7,6 @@
boot.loader.generic-extlinux-compatible.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
# sane.persist.enable = false; # TODO: disable (but run `nix flake check` to ensure it works!)
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
system.stateVersion = "21.05";

View File

@@ -4,6 +4,7 @@
imports = [
./fs.nix
./net.nix
./secrets.nix
./services
];
@@ -14,13 +15,9 @@
signaldctl.enableFor.user.colin = true;
};
sane.roles.ac = true;
sane.roles.build-machine.enable = true;
sane.roles.build-machine.emulation = false;
sane.zsh.showDeadlines = false; # ~/knowledge doesn't always exist
sane.persist.enable = true;
sane.services.dyn-dns.enable = true;
sane.services.wg-home.enable = true;
sane.services.wg-home.enableWan = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade

View File

@@ -3,12 +3,6 @@
{
networking.domain = "uninsane.org";
sane.ports.openFirewall = true;
sane.ports.openUpnp = true;
# view refused packets with: `sudo journalctl -k`
# networking.firewall.logRefusedPackets = true;
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
# Per-interface useDHCP will be mandatory in the future, so this generated config
# replicates the default behaviour.
@@ -17,6 +11,9 @@
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
networking.wireless.enable = false;
# networking.firewall.enable = false;
networking.firewall.enable = true;
# this is needed to forward packets from the VPN to the host
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
@@ -36,14 +33,6 @@
# - getent ahostsv4 www.google.com
# - try fix: <https://serverfault.com/questions/765989/connect-to-3rd-party-vpn-server-but-dont-use-it-as-the-default-route/766290#766290>
services.resolved.enable = true;
# without DNSSEC:
# - dig matrix.org => works
# - curl https://matrix.org => works
# with default DNSSEC:
# - dig matrix.org => works
# - curl https://matrix.org => fails
# i don't know why. this might somehow be interfering with the DNS run on this device (trust-dns)
services.resolved.dnssec = "false";
networking.nameservers = [
# use systemd-resolved resolver
# full resolver (which understands /etc/hosts) lives on 127.0.0.53
@@ -156,9 +145,9 @@
# we also bridge DNS traffic
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}
-j DNAT --to-destination ${veth-host-ip}:53
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}
-j DNAT --to-destination ${veth-host-ip}:53
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
# - alternatively, we could fix DNS servers like 1.1.1.1.

View File

@@ -0,0 +1,41 @@
{ ... }:
{
sops.secrets."ddns_afraid" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."ddns_he" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."dovecot_passwd" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."duplicity_passphrase" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."freshrss_passwd" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."matrix_synapse_secrets" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."mautrix_signal_env" = {
sopsFile = ../../../secrets/servo/mautrix_signal_env.bin;
};
sops.secrets."mediawiki_pw" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."pleroma_secrets" = {
sopsFile = ../../../secrets/servo.yaml;
};
sops.secrets."wg_ovpns_privkey" = {
sopsFile = ../../../secrets/servo.yaml;
};
}

View File

@@ -1,34 +0,0 @@
{ config, lib, ... }:
let
cweb-cfg = config.services.calibre-web;
inherit (cweb-cfg) user group;
inherit (cweb-cfg.listen) ip port;
svc-dir = "/var/lib/${cweb-cfg.dataDir}";
in
# XXX: disabled because of runtime errors like:
# > File "/nix/store/c7jqvx980nlg9xhxi065cba61r2ain9y-calibre-web-0.6.19/lib/python3.10/site-packages/calibreweb/cps/db.py", line 926, in speaking_language
# > languages = self.session.query(Languages) \
# > AttributeError: 'NoneType' object has no attribute 'query'
lib.mkIf false
{
sane.persist.sys.plaintext = [
{ inherit user group; mode = "0700"; directory = svc-dir; }
];
services.calibre-web.enable = true;
services.calibre-web.listen.ip = "127.0.0.1";
# XXX: externally populate `${svc-dir}/metadata.db` (once) from
# <https://github.com/janeczku/calibre-web/blob/master/library/metadata.db>
# i don't know why you have to do this??
# services.calibre-web.options.calibreLibrary = svc-dir;
services.nginx.virtualHosts."calibre.uninsane.org" = {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${ip}:${builtins.toString port}";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
}

View File

@@ -6,7 +6,7 @@ lib.mkIf false
systemd.services.ddns-afraid = {
description = "update dynamic DNS entries for freedns.afraid.org";
serviceConfig = {
EnvironmentFile = config.sops.secrets."ddns_afraid.env".path;
EnvironmentFile = config.sops.secrets.ddns_afraid.path;
# TODO: ProtectSystem = "strict";
# TODO: ProtectHome = "full";
# TODO: PrivateTmp = true;

View File

@@ -6,7 +6,7 @@ lib.mkIf false
systemd.services.ddns-he = {
description = "update dynamic DNS entries for HurricaneElectric";
serviceConfig = {
EnvironmentFile = config.sops.secrets."ddns_he.env".path;
EnvironmentFile = config.sops.secrets.ddns_he.path;
# TODO: ProtectSystem = "strict";
# TODO: ProtectHome = "full";
# TODO: PrivateTmp = true;

View File

@@ -1,10 +1,8 @@
{ ... }:
{
imports = [
./calibre.nix
./ddns-afraid.nix
./ddns-he.nix
./email
./ejabberd.nix
./freshrss.nix
./gitea.nix
@@ -13,14 +11,12 @@
./jackett.nix
./jellyfin.nix
./kiwix-serve.nix
./komga.nix
./lemmy.nix
./matrix
./navidrome.nix
./nixserve.nix
./nginx.nix
./pict-rs.nix
./pleroma.nix
./postfix.nix
./postgres.nix
./prosody.nix
./transmission.nix

View File

@@ -22,67 +22,27 @@
sane.persist.sys.plaintext = [
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
];
sane.ports.ports."3478" = {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn";
};
sane.ports.ports."5222" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-client-to-server";
};
sane.ports.ports."5223" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpps-client-to-server"; # XMPP over TLS
};
sane.ports.ports."5269" = {
protocol = [ "tcp" ];
visibleTo.wan = true;
description = "colin-xmpp-server-to-server";
};
sane.ports.ports."5270" = {
protocol = [ "tcp" ];
visibleTo.wan = true;
description = "colin-xmpps-server-to-server"; # XMPP over TLS
};
sane.ports.ports."5280" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh";
};
sane.ports.ports."5281" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh-https";
};
sane.ports.ports."5349" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn-over-tls";
};
sane.ports.ports."5443" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
};
# TODO: forward these TURN ports!
networking.firewall.allowedTCPPorts = [
3478 # STUN/TURN
5222 # XMPP client -> server
5223 # XMPPS client -> server (XMPP over TLS)
5269 # XMPP server -> server
5270 # XMPPS server -> server (XMPP over TLS)
5280 # bosh
5281 # bosh (https) ??
5349 # STUN/TURN (TLS)
5443 # web services (file uploads, websockets, admin)
];
networking.firewall.allowedUDPPorts = [
3478 # STUN/TURN
];
networking.firewall.allowedTCPPortRanges = [{
from = 49152; # TURN
to = 49408;
to = 65535;
}];
networking.firewall.allowedUDPPortRanges = [{
from = 49152; # TURN
to = 49408;
to = 65535;
}];
# provide access to certs
@@ -117,7 +77,7 @@
sane.services.trust-dns.zones."uninsane.org".inet = {
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
A."xmpp" = "%ANATIVE%";
A."xmpp" = "%NATIVE%";
CNAME."muc.xmpp" = "xmpp";
CNAME."pubsub.xmpp" = "xmpp";
CNAME."upload.xmpp" = "xmpp";
@@ -274,7 +234,7 @@
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %ANATIVE%
turn_ipv4_address: %NATIVE%
-
# STUN+TURN UDP
port: 3478
@@ -283,7 +243,7 @@
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %ANATIVE%
turn_ipv4_address: %NATIVE%
-
# STUN+TURN TLS over TCP
port: 5349
@@ -294,7 +254,7 @@
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %ANATIVE%
turn_ipv4_address: %NATIVE%
# TODO: enable mod_fail2ban
# TODO(low): look into mod_http_fileserver for serving macros?
@@ -427,7 +387,7 @@
# config is 444 (not 644), so we want to write out-of-place and then atomically move
# TODO: factor this out into `sane-woop` helper?
rm -f /var/lib/ejabberd/ejabberd.yaml.new
${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
'';

View File

@@ -1,37 +0,0 @@
# nix configs to reference:
# - <https://gitlab.com/simple-nixos-mailserver/nixos-mailserver>
# - <https://github.com/nix-community/nur-combined/-/tree/master/repos/eh5/machines/srv-m/mail-rspamd.nix>
# - postfix / dovecot / rspamd / stalwart-jmap / sogo
#
# rspamd:
# - nixos: <https://nixos.wiki/wiki/Rspamd>
# - guide: <https://rspamd.com/doc/quickstart.html>
# - non-nixos example: <https://dataswamp.org/~solene/2021-07-13-smtpd-rspamd.html>
#
#
# my rough understanding of the pieces:
# - postfix handles SMTP protocol with the rest of the world.
# - dovecot implements IMAP protocol.
# - client auth (i.e. validate that user@uninsane.org is who they claim)
# - "folders" (INBOX, JUNK) are internal to dovecot?
# or where do folders live, on-disk?
#
# - non-local clients (i.e. me) interact with BOTH postfix and dovecot, but primarily dovecot:
# - mail reading is done via IMAP (so, dovecot)
# - mail sending is done via SMTP/submission port (so, postfix)
# - but postfix delegates authorization of that outgoing mail to dovecot, on the server side
#
# - local clients (i.e. sendmail) interact only with postfix
{ ... }:
{
imports = [
./dovecot.nix
./postfix.nix
];
#### SPAM FILTERING
# services.rspamd.enable = true;
# services.rspamd.postfix.enable = true;
}

View File

@@ -1,142 +0,0 @@
# dovecot config options: <https://doc.dovecot.org/configuration_manual/>
#
# sieve docs:
# - sieve language examples: <https://doc.dovecot.org/configuration_manual/sieve/examples/>
# - sieve protocol/language: <https://proton.me/support/sieve-advanced-custom-filters>
{ config, lib, pkgs, ... }:
{
sane.ports.ports."143" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-imap-imap.uninsane.org";
};
sane.ports.ports."993" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-imaps-imap.uninsane.org";
};
# exists only to manage certs for dovecot
services.nginx.virtualHosts."imap.uninsane.org" = {
enableACME = true;
};
sane.services.trust-dns.zones."uninsane.org".inet = {
CNAME."imap" = "native";
};
sops.secrets."dovecot_passwd" = {
owner = config.users.users.dovecot2.name;
# TODO: debug why mail can't be sent without this being world-readable
mode = "0444";
};
# inspired by https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/
services.dovecot2.enable = true;
# services.dovecot2.enableLmtp = true;
services.dovecot2.sslServerCert = "/var/lib/acme/imap.uninsane.org/fullchain.pem";
services.dovecot2.sslServerKey = "/var/lib/acme/imap.uninsane.org/key.pem";
services.dovecot2.enablePAM = false;
# sieve scripts require me to set a user for... idk why?
services.dovecot2.mailUser = "colin";
services.dovecot2.mailGroup = "users";
users.users.colin.isSystemUser = lib.mkForce false;
services.dovecot2.extraConfig =
let
passwdFile = config.sops.secrets.dovecot_passwd.path;
in
''
passdb {
driver = passwd-file
args = ${passwdFile}
}
userdb {
driver = passwd-file
args = ${passwdFile}
}
# allow postfix to query our auth db
service auth {
unix_listener auth {
mode = 0660
user = postfix
group = postfix
}
}
auth_mechanisms = plain login
# accept incoming messaging from postfix
# service lmtp {
# unix_listener dovecot-lmtp {
# mode = 0600
# user = postfix
# group = postfix
# }
# }
# plugin {
# sieve_plugins = sieve_imapsieve
# }
mail_debug = yes
auth_debug = yes
# verbose_ssl = yes
'';
services.dovecot2.mailboxes = {
# special-purpose mailboxes: "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash"
# RFC6154 describes these special mailboxes: https://www.ietf.org/rfc/rfc6154.html
# how these boxes are treated is 100% up to the client and server to decide.
# client behavior:
# iOS
# - Drafts: ?
# - Sent: works
# - Trash: works
# - Junk: works ("mark" -> "move to Junk")
# aerc
# - Drafts: works
# - Sent: works
# - Trash: no; deleted messages are actually deleted
# use `:move trash` instead
# - Junk: ?
# Sent mailbox: all sent messages are copied to it. unclear if this happens server-side or client-side.
Drafts = { specialUse = "Drafts"; auto = "create"; };
Sent = { specialUse = "Sent"; auto = "create"; };
Trash = { specialUse = "Trash"; auto = "create"; };
Junk = { specialUse = "Junk"; auto = "create"; };
};
services.dovecot2.mailPlugins = {
perProtocol = {
# imap.enable = [
# "imap_sieve"
# ];
lda.enable = [
"sieve"
];
# lmtp.enable = [
# "sieve"
# ];
};
};
services.dovecot2.modules = [
pkgs.dovecot_pigeonhole # enables sieve execution (?)
];
services.dovecot2.sieveScripts = {
# if any messages fail to pass (or lack) DKIM, move them to Junk
# XXX the key name ("after") is only used to order sieve execution/ordering
after = builtins.toFile "ensuredkim.sieve" ''
require "fileinto";
if not header :contains "Authentication-Results" "dkim=pass" {
fileinto "Junk";
stop;
}
'';
};
}

View File

@@ -1,4 +1,3 @@
# config options: <https://docs.gitea.io/en-us/administration/config-cheat-sheet/>
{ config, pkgs, lib, ... }:
{
@@ -11,6 +10,9 @@
services.gitea.database.type = "postgres";
services.gitea.database.user = "git";
services.gitea.appName = "Perfectly Sane Git";
services.gitea.domain = "git.uninsane.org";
services.gitea.rootUrl = "https://git.uninsane.org/";
services.gitea.settings.session.COOKIE_SECURE = true;
# services.gitea.disableRegistration = true;
# gitea doesn't create the git user
@@ -25,13 +27,9 @@
};
services.gitea.settings = {
# options: "Trace", "Debug", "Info", "Warn", "Error", "Critical"
log.LEVEL = "Warn";
server = {
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
LANDING_PAGE = "explore";
DOMAIN = "git.uninsane.org";
ROOT_URL = "https://git.uninsane.org/";
};
service = {
# timeout for email approval. 5760 = 4 days
@@ -46,7 +44,6 @@
ENABLE_CAPTCHA = true;
NOREPLY_ADDRESS = "noreply.anonymous.git@uninsane.org";
};
session.COOKIE_SECURE = true;
repository = {
DEFAULT_BRANCH = "master";
};
@@ -61,8 +58,6 @@
};
#"ui.meta" = ... to customize html author/description/etc
mailer = {
# alternative is to use nixos-level config:
# services.gitea.mailerPasswordFile = ...
ENABLED = true;
MAILER_TYPE = "sendmail";
FROM = "notify.git@uninsane.org";
@@ -74,6 +69,8 @@
FORMAT = "RFC3339";
};
};
# options: "Trace", "Debug", "Info", "Warn", "Error", "Critical"
services.gitea.settings.log.LEVEL = "Warn";
systemd.services.gitea.serviceConfig = {
# nix default is AF_UNIX AF_INET AF_INET6.
@@ -99,11 +96,4 @@
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
sane.ports.ports."22" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-git@git.uninsane.org";
};
}

View File

@@ -1,76 +1,16 @@
# configuration options (today i don't store my config in nix):
#
# - jellyfin-web can be statically configured (result/share/jellyfin-web/config.json)
# - <https://jellyfin.org/docs/general/clients/web-config>
# - configure server list, plugins, "menuLinks", colors
#
# - jellfyin server is configured in /var/lib/jellfin/
# - root/default/<LibraryType>/
# - <LibraryName>.mblink: contains the directory name where this library lives
# - options.xml: contains preferences which were defined in the web UI during import
# - e.g. `EnablePhotos`, `EnableChapterImageExtraction`, etc.
# - config/encoding.xml: transcoder settings
# - config/system.xml: misc preferences like log file duration, audiobook resume settings, etc.
# - data/jellyfin.db: maybe account definitions? internal state?
{ config, lib, ... }:
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
# else it's too spammy
lib.mkIf false
{
# https://jellyfin.org/docs/general/networking/index.html
sane.ports.ports."1900" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-upnp-for-jellyfin";
};
sane.ports.ports."7359" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "colin-jellyfin-specific-client-discovery";
# ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall
};
# not sure if 8096/8920 get used either:
sane.ports.ports."8096" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-http-lan";
};
sane.ports.ports."8920" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-jellyfin-https-lan";
};
sane.persist.sys.plaintext = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; directory = "/var/lib/jellyfin"; }
networking.firewall.allowedUDPPorts = [
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
];
sane.persist.sys.plaintext = [
# TODO: mode? could be more granular
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
];
sane.fs."/var/lib/jellyfin/config/logging.json" = {
# "Emby.Dlna" logging: <https://jellyfin.org/docs/general/networking/dlna>
symlink.text = ''
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"Emby.Dlna": "Debug",
"Emby.Dlna.Eventing": "Debug"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithThreadId" ]
}
}
'';
wantedBeforeBy = [ "jellyfin.service" ];
};
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs

View File

@@ -1,22 +0,0 @@
{ config, ... }:
let
svc-cfg = config.services.komga;
inherit (svc-cfg) user group port stateDir;
in
{
sane.persist.sys.plaintext = [
{ inherit user group; mode = "0700"; directory = stateDir; }
];
services.komga.enable = true;
services.komga.port = 11319; # chosen at random
services.nginx.virtualHosts."komga.uninsane.org" = {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${builtins.toString port}";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."komga" = "native";
}

View File

@@ -1,58 +0,0 @@
# docs:
# - <repo:LemmyNet/lemmy:docker/federation/nginx.conf>
# - <repo:LemmyNet/lemmy:docker/nginx.conf>
# - <repo:LemmyNet/lemmy-ansible:templates/nginx.conf>
{ config, lib, ... }:
let
inherit (builtins) toString;
inherit (lib) mkForce;
uiPort = 1234; # default ui port is 1234
backendPort = 8536; # default backend port is 8536
# - i guess the "backend" port is used for federation?
in {
services.lemmy = {
enable = true;
settings.hostname = "lemmy.uninsane.org";
settings.federation.enabled = true;
# federation.debug forces outbound federation queries to be run synchronously
# settings.federation.debug = true;
settings.port = backendPort;
ui.port = uiPort;
database.createLocally = true;
nginx.enable = true;
};
systemd.services.lemmy.serviceConfig = {
# fix to use a normal user so we can configure perms correctly
DynamicUser = mkForce false;
User = "lemmy";
Group = "lemmy";
};
systemd.services.lemmy.environment = {
RUST_BACKTRACE = "full";
# RUST_LOG = "debug";
# upstream defaults LEMMY_DATABASE_URL = "postgres:///lemmy?host=/run/postgresql";
# - Postgres complains that we didn't specify a user
# lemmy formats the url as:
# - postgres://{user}:{password}@{host}:{port}/{database}
# SO suggests (https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url):
# - postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
# LEMMY_DATABASE_URL = "postgres://lemmy@/run/postgresql"; # connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL: database "run/postgresql" does not exist
# LEMMY_DATABASE_URL = "postgres://lemmy?host=/run/postgresql"; # no PostgreSQL user name specified in startup packet
# LEMMY_DATABASE_URL = mkForce "postgres://lemmy@?host=/run/postgresql"; # WORKS
LEMMY_DATABASE_URL = mkForce "postgres://lemmy@/lemmy?host=/run/postgresql";
};
users.groups.lemmy = {};
users.users.lemmy = {
group = "lemmy";
isSystemUser = true;
};
services.nginx.virtualHosts."lemmy.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
}

View File

@@ -1,15 +1,17 @@
# docs: <https://nixos.wiki/wiki/Matrix>
# docs: <https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse>
# example config: <https://github.com/matrix-org/synapse/blob/develop/docs/sample_config.yaml>
# docs: https://nixos.wiki/wiki/Matrix
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
{ config, lib, pkgs, ... }:
{
imports = [
./discord-puppet.nix
./irc.nix
# ./irc.nix
./signal.nix
];
# allow synapse to read the registration files of its appservices
users.users.matrix-synapse.extraGroups = [ "mautrix-signal" ];
sane.persist.sys.plaintext = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
];
@@ -42,14 +44,11 @@
}
];
services.matrix-synapse.settings.x_forwarded = true; # because we proxy matrix behind nginx
services.matrix-synapse.settings.max_upload_size = "100M"; # default is "50M"
services.matrix-synapse.settings.admin_contact = "admin.matrix@uninsane.org";
services.matrix-synapse.settings.registrations_require_3pid = [ "email" ];
services.matrix-synapse.extraConfigFiles = [
config.sops.secrets."matrix_synapse_secrets.yaml".path
config.sops.secrets.matrix_synapse_secrets.path
];
# services.matrix-synapse.extraConfigFiles = [builtins.toFile "matrix-synapse-extra-config" ''
@@ -98,10 +97,6 @@
locations."/" = {
proxyPass = "http://127.0.0.1:8008";
extraConfig = ''
# allow uploading large files (matrix enforces a separate limit, downstream)
client_max_body_size 512m;
'';
};
# redirect browsers to the web client.
# i don't think native matrix clients ever fetch the root.
@@ -138,7 +133,7 @@
};
sops.secrets."matrix_synapse_secrets.yaml" = {
sops.secrets."matrix_synapse_secrets" = {
owner = config.users.users.matrix-synapse.name;
};
}

View File

@@ -1,9 +1,4 @@
{ lib, ... }:
# XXX mx-discord-puppet uses nodejs_14 which is EOL
# - mx-discord-puppet is abandoned upstream _and_ in nixpkgs
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
lib.mkIf false
{
sane.persist.sys.plaintext = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }

View File

@@ -1,13 +0,0 @@
diff --git a/src/irc/ConnectionInstance.ts b/src/irc/ConnectionInstance.ts
index 688036ca..3373fa27 100644
--- a/src/irc/ConnectionInstance.ts
+++ b/src/irc/ConnectionInstance.ts
@@ -149,7 +149,7 @@ export class ConnectionInstance {
if (this.dead) {
return Promise.resolve();
}
- ircReason = ircReason || reason;
+ ircReason = "bye"; // don't reveal through the IRC quit message that we're a bridge
log.info(
"disconnect()ing %s@%s - %s", this.nick, this.domain, reason
);

View File

@@ -1,50 +0,0 @@
diff --git a/config.schema.yml b/config.schema.yml
index 2e71c8d6..42ba8ba1 100644
--- a/config.schema.yml
+++ b/config.schema.yml
@@ -433,7 +433,7 @@ properties:
type: "boolean"
realnameFormat:
type: "string"
- enum: ["mxid","reverse-mxid"]
+ enum: ["mxid","reverse-mxid","localpart"]
ipv6:
type: "object"
properties:
diff --git a/src/irc/IdentGenerator.ts b/src/irc/IdentGenerator.ts
index 7a2b5cf1..50f7815a 100644
--- a/src/irc/IdentGenerator.ts
+++ b/src/irc/IdentGenerator.ts
@@ -74,6 +74,9 @@ export class IdentGenerator {
else if (server.getRealNameFormat() === "reverse-mxid") {
realname = IdentGenerator.sanitiseRealname(IdentGenerator.switchAroundMxid(matrixUser));
}
+ else if (server.getRealNameFormat() == "localpart") {
+ realname = IdentGenerator.sanitiseRealname(matrixUser.localpart);
+ }
else {
throw Error('Invalid value for realNameFormat');
}
diff --git a/src/irc/IrcServer.ts b/src/irc/IrcServer.ts
index 2af73ab4..895b9783 100644
--- a/src/irc/IrcServer.ts
+++ b/src/irc/IrcServer.ts
@@ -101,7 +101,7 @@ export interface IrcServerConfig {
};
lineLimit: number;
userModes?: string;
- realnameFormat?: "mxid"|"reverse-mxid";
+ realnameFormat?: "mxid"|"reverse-mxid"|"localpart";
pingTimeoutMs: number;
pingRateMs: number;
kickOn: {
@@ -289,7 +289,7 @@ export class IrcServer {
return this.config.ircClients.userModes || "";
}
- public getRealNameFormat(): "mxid"|"reverse-mxid" {
+ public getRealNameFormat(): "mxid"|"reverse-mxid"|"localpart" {
return this.config.ircClients.realnameFormat || "mxid";
}

View File

@@ -1,119 +1,21 @@
# config docs:
# - <https://github.com/matrix-org/matrix-appservice-irc/blob/develop/config.sample.yaml>
# TODO: /quit message for bridged users reveals to IRC users that i'm using a bridge;
# probably want to remove that.
{ config, lib, ... }:
let
ircServer = { name, additionalAddresses ? [], sasl ? true }: let
lowerName = lib.toLower name;
in {
# XXX sasl: appservice doesn't support NickServ identification (only SASL, or PASS if sasl = false)
inherit name additionalAddresses sasl;
port = 6697;
ssl = true;
botConfig = {
# bot has no presence in IRC channel; only real Matrix users
enabled = false;
# this is the IRC username/nickname *of the bot* (not visible in channels): not of the end-user.
# the irc username/nick of a mapped Matrix user is determined further down in `ircClients` section.
# if `enabled` is false, then this name probably never shows up on the IRC side (?)
nick = "uninsane";
username = "uninsane";
joinChannelsIfNoUsers = false;
};
dynamicChannels = {
enabled = true;
aliasTemplate = "#irc_${lowerName}_$CHANNEL";
published = false; # false => irc rooms aren't listed in homeserver public rooms list
federate = false; # false => Matrix users from other homeservers can't join IRC channels
};
ircClients = {
nickTemplate = "$LOCALPARTsane"; # @colin:uninsane.org (Matrix) -> colinsane (IRC)
realnameFormat = "reverse-mxid"; # @colin:uninsane.org (Matrix) -> org.uninsane:colin (IRC)
# realnameFormat = "localpart"; # @colin:uninsane.org (Matrix) -> colin (IRC) -- but requires the mxid patch below
# by default, Matrix will convert messages greater than (3) lines into a pastebin-like URL to send to IRC.
lineLimit = 20;
# Rizon in particular allows only 4 connections from one IP before a 30min ban.
# that's effectively reduced to 2 during a netsplit, or maybe during a restart.
# - https://wiki.rizon.net/index.php?title=Connection/Session_Limit_Exemptions
# especially, misconfigurations elsewhere in this config may cause hundreds of connections
# so this is a safeguard.
maxClients = 2;
# don't have the bridge disconnect me from IRC when idle.
idleTimeout = 0;
concurrentReconnectLimit = 2;
reconnectIntervalMs = 60000;
kickOn = {
# remove Matrix user from room when...
channelJoinFailure = false;
ircConnectionFailure = false;
userQuit = true;
};
};
matrixClients = {
userTemplate = "@irc_${lowerName}_$NICK"; # the :uninsane.org part is appended automatically
};
# this will let this user message the appservice with `!join #<IRCChannel>` and the rest "Just Works"
"@colin:uninsane.org" = "admin";
membershipLists = {
enabled = true;
global = {
ircToMatrix = {
initial = true;
incremental = true;
requireMatrixJoined = false;
};
matrixToIrc = {
initial = true;
incremental = true;
};
};
ignoreIdleUsersOnStartup = {
enabled = false; # false => always bridge users, even if idle
};
};
# sync room description?
bridgeInfoState = {
enabled = true;
initial = true;
};
# for per-user IRC password:
# - invite @irc_${lowerName}_NickServ:uninsane.org to a DM and type `help` => register
# - invite the matrix-appservice-irc user to a DM and type `!help` => add PW to database
# to validate that i'm authenticated on the IRC network, DM @irc_${lowerName}_NickServ:uninsane.org:
# - send: `STATUS colinsane`
# - response should be `3`: "user recognized as owner via password identification"
# passwordEncryptionKeyPath = "/path/to/privkey"; # appservice will generate its own if unspecified
};
in
{
nixpkgs.overlays = [
(next: prev: {
matrix-appservice-irc = prev.matrix-appservice-irc.overrideAttrs (super: {
patches = super.patches or [] ++ [
./irc-no-reveal-bridge.patch
# ./irc-no-reveal-mxid.patch
];
});
})
];
sane.persist.sys.plaintext = [
# TODO: mode?
{ user = "matrix-appservice-irc"; group = "matrix-appservice-irc"; directory = "/var/lib/matrix-appservice-irc"; }
# user and group are both "matrix-appservice-irc"
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
];
services.matrix-synapse.settings.app_service_config_files = [
"/var/lib/matrix-appservice-irc/registration.yml" # auto-created by irc appservice
];
# note: Rizon allows only FOUR simultaneous IRC connections per IP: https://wiki.rizon.net/index.php?title=Connection/Session_Limit_Exemptions
# Rizon supports CertFP for auth: https://wiki.rizon.net/index.php?title=CertFP
services.matrix-appservice-irc.enable = true;
services.matrix-appservice-irc.registrationUrl = "http://127.0.0.1:8009";
# settings documented here: https://github.com/matrix-org/matrix-appservice-irc/blob/develop/config.sample.yaml
services.matrix-appservice-irc.settings = {
homeserver = {
url = "http://127.0.0.1:8008";
@@ -126,30 +28,69 @@ in
ircService = {
servers = {
"irc.esper.net" = ircServer {
name = "esper";
sasl = false;
# notable channels:
# - #merveilles
"irc.rizon.net" = {
name = "Rizon";
port = 6697; # SSL port
ssl = true;
sasl = true; # appservice doesn't support NickServ identification
botConfig = {
# bot has no presence in IRC channel; only real Matrix users
enabled = false;
# nick = "UninsaneDotOrg";
nick = "uninsane";
username = "uninsane";
};
dynamicChannels = {
enabled = true;
aliasTemplate = "#irc_rizon_$CHANNEL";
};
ircClients = {
nickTemplate = "$LOCALPARTsane";
# by default, Matrix will convert messages greater than (3) lines into a pastebin-like URL to send to IRC.
lineLimit = 20;
};
matrixClients = {
userTemplate = "@irc_rizon_$NICK"; # the :uninsane.org part is appended automatically
};
# this will let this user message the appservice with `!join #<IRCChannel>` and the rest "Just Works"
"@colin:uninsane.org" = "admin";
membershipLists = {
enabled = true;
global = {
ircToMatrix = {
initial = true;
incremental = true;
requireMatrixJoined = false;
};
matrixToIrc = {
initial = true;
incremental = true;
};
};
};
# sync room description?
bridgeInfoState = {
enabled = true;
initial = true;
};
# hardcoded mappings, for when dynamicChannels fails us. TODO: probably safe to remove these.
# mappings = {
# "#chat" = {
# roomIds = [ "!GXJSOTdbtxRboGtDep:uninsane.org" ];
# };
# # BakaBT requires account registration, which i think means my user needs to be added before the appservice user
# "#BakaBT" = {
# roomIds = [ "!feZKttuYuHilqPFSkD:uninsane.org" ];
# };
# };
# for per-user IRC password:
# invite @irc_rizon_NickServ:uninsane.org to a DM and type `help` => register
# invite the matrix-appservice-irc user to a DM and type `!help` => add PW to database
# passwordEncryptionKeyPath = "/path/to/privkey"; # appservice will generate its own if unspecified
};
"irc.libera.chat" = ircServer {
name = "libera";
sasl = false;
# notable channels:
# - #hare
};
"irc.myanonamouse.net" = ircServer {
name = "MyAnonamouse";
additionalAddresses = [ "irc2.myanonamouse.net" ];
sasl = false;
};
"irc.oftc.net" = ircServer {
name = "oftc";
# notable channels:
# - #sxmo
# - #sxmo-offtopic
};
"irc.rizon.net" = ircServer { name = "Rizon"; };
};
};
};

View File

@@ -7,9 +7,6 @@
{ user = "signald"; group = "signald"; directory = "/var/lib/signald"; }
];
# allow synapse to read the registration file
users.users.matrix-synapse.extraGroups = [ "mautrix-signal" ];
services.signald.enable = true;
services.mautrix-signal.enable = true;
services.mautrix-signal.environmentFile =
@@ -30,6 +27,7 @@
};
sops.secrets."mautrix_signal_env" = {
format = "binary";
mode = "0440";
owner = config.users.users.mautrix-signal.name;
group = config.users.users.matrix-synapse.name;

View File

@@ -13,19 +13,7 @@ let
in
{
sane.ports.ports."80" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
description = "colin-http-uninsane.org";
};
sane.ports.ports."443" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-https-uninsane.org";
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.nginx.enable = true;
services.nginx.appendConfig = ''

View File

@@ -17,5 +17,5 @@
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
sane.services.nixserve.enable = true;
sane.services.nixserve.secretKeyFile = config.sops.secrets.nix_serve_privkey.path;
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
}

View File

@@ -1,23 +0,0 @@
# pict-rs is an image database/store used by Lemmy.
# i don't explicitly activate it here -- just adjust its defaults to be a bit friendlier
{ config, lib, ... }:
let
cfg = config.services.pict-rs;
in
{
sane.persist.sys.plaintext = lib.mkIf cfg.enable [
{ user = "pict-rs"; group = "pict-rs"; directory = cfg.dataDir; }
];
systemd.services.pict-rs.serviceConfig = {
# fix to use a normal user so we can configure perms correctly
DynamicUser = lib.mkForce false;
User = "pict-rs";
Group = "pict-rs";
};
users.groups.pict-rs = {};
users.users.pict-rs = {
group = "pict-rs";
isSystemUser = true;
};
}

View File

@@ -1,6 +1,7 @@
# postfix config options: <https://www.postfix.org/postconf.5.html>
# DOCS:
# - dovecot config: <https://doc.dovecot.org/configuration_manual/>
{ lib, pkgs, ... }:
{ config, lib, ... }:
let
submissionOptions = {
@@ -28,22 +29,18 @@ in
# "/var/lib/dovecot"
];
sane.ports.ports."25" = {
protocol = [ "tcp" ];
visibleTo.ovpn = true;
description = "colin-smtp-mx.uninsane.org";
};
sane.ports.ports."465" = {
protocol = [ "tcp" ];
visibleTo.ovpn = true;
description = "colin-smtps-mx.uninsane.org";
};
sane.ports.ports."587" = {
protocol = [ "tcp" ];
visibleTo.ovpn = true;
description = "colin-smtps-submission-mx.uninsane.org";
};
networking.firewall.allowedTCPPorts = [
25 # SMTP
143 # IMAP
465 # SMTPS
587 # SMTPS/submission
993 # IMAPS
];
# exists only to manage certs for dovecot
services.nginx.virtualHosts."imap.uninsane.org" = {
enableACME = true;
};
# exists only to manage certs for Postfix
services.nginx.virtualHosts."mx.uninsane.org" = {
enableACME = true;
@@ -54,6 +51,7 @@ in
MX."@" = "10 mx.uninsane.org.";
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
A."mx" = "185.157.162.178";
CNAME."imap" = "native";
# Sender Policy Framework:
# +mx => mail passes if it originated from the MX
@@ -64,7 +62,7 @@ in
# DKIM public key:
TXT."mx._domainkey" =
"v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkSyMufc2KrRx3j17e/LyB+3eYSBRuEFT8PUka8EDX04QzCwDPdkwgnj3GNDvnB5Ktb05Cf2SJ/S1OLqNsINxJRWtkVfZd/C339KNh9wrukMKRKNELL9HLUw0bczOI4gKKFqyrRE9qm+4csCMAR79Te9FCjGV/jVnrkLdPT0GtFwIDAQAB"
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkSyMufc2KrRx3j17e/LyB+3eYSBRuEFT8PUka8EDX04QzCwDPdkwgnj3GNDvnB5Ktb05Cf2SJ/S1OLqNsINxJRWtkVfZd/C339KNh9wrukMKRKNELL9HLUw0bczOI4gKKFqyrRE9qm+4csCMAR79Te9FCjGV/jVnrkLdPT0GtFwIDAQAB"
;
# DMARC fields <https://datatracker.ietf.org/doc/html/rfc7489>:
@@ -97,40 +95,18 @@ in
@uninsane.org colin
'';
services.postfix.config = {
services.postfix.extraConfig = ''
# smtpd_milters = local:/run/opendkim/opendkim.sock
# milter docs: http://www.postfix.org/MILTER_README.html
# mail filters for receiving email and from authorized SMTP clients (i.e. via submission)
# mail filters for receiving email and authorized SMTP clients
# smtpd_milters = inet:185.157.162.190:8891
# opendkim.sock will add a Authentication-Results header, with `dkim=pass|fail|...` value to received messages
smtpd_milters = "unix:/run/opendkim/opendkim.sock";
smtpd_milters = unix:/run/opendkim/opendkim.sock
# mail filters for sendmail
non_smtpd_milters = "$smtpd_milters";
# what to do when a milter exits unexpectedly:
milter_default_action = "accept";
inet_protocols = "ipv4";
smtp_tls_security_level = "may";
# hand received mail over to dovecot so that it can run sieves & such
mailbox_command = ''${pkgs.dovecot}/libexec/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT"'';
# hand received mail over to dovecot
# virtual_alias_maps = [
# "hash:/etc/postfix/virtual"
# ];
# mydestination = "";
# virtual_mailbox_domains = [ "localhost" "uninsane.org" ];
# # virtual_mailbox_maps = "hash:/etc/postfix/virtual";
# virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
# anti-spam options: <https://www.postfix.org/SMTPD_ACCESS_README.html>
# reject_unknown_sender_domain: causes postfix to `dig <sender> MX` and make sure that exists.
# but may cause problems receiving mail from google & others who load-balance?
# - <https://unix.stackexchange.com/questions/592131/how-to-reject-email-from-unknown-domains-with-postfix-on-centos>
# smtpd_sender_restrictions = reject_unknown_sender_domain
};
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
inet_protocols = ipv4
smtp_tls_security_level = may
'';
services.postfix.enableSubmission = true;
services.postfix.submissionOptions = submissionOptions;
@@ -145,8 +121,6 @@ in
};
#### OPENDKIM
services.opendkim.enable = true;
# services.opendkim.domains = "csl:uninsane.org";
services.opendkim.domains = "uninsane.org";
@@ -170,6 +144,59 @@ in
UMask = lib.mkForce "0011";
};
# inspired by https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/
services.dovecot2.enable = true;
services.dovecot2.mailboxes = {
# special-purpose mailboxes: "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash"
# RFC6154 describes these special mailboxes: https://www.ietf.org/rfc/rfc6154.html
# how these boxes are treated is 100% up to the client and server to decide.
# client behavior:
# iOS
# - Drafts: ?
# - Sent: works
# - Trash: works
# aerc
# - Drafts: works
# - Sent: works
# - Trash: no; deleted messages are actually deleted
# use `:move trash` instead
# Sent mailbox: all sent messages are copied to it. unclear if this happens server-side or client-side.
Drafts = { specialUse = "Drafts"; auto = "create"; };
Sent = { specialUse = "Sent"; auto = "create"; };
Trash = { specialUse = "Trash"; auto = "create"; };
};
services.dovecot2.sslServerCert = "/var/lib/acme/imap.uninsane.org/fullchain.pem";
services.dovecot2.sslServerKey = "/var/lib/acme/imap.uninsane.org/key.pem";
services.dovecot2.enablePAM = false;
services.dovecot2.extraConfig =
let
passwdFile = config.sops.secrets.dovecot_passwd.path;
in
''
passdb {
driver = passwd-file
args = ${passwdFile}
}
userdb {
driver = passwd-file
args = ${passwdFile}
}
# allow postfix to query our auth db
service auth {
unix_listener auth {
mode = 0660
user = postfix
group = postfix
}
}
auth_mechanisms = plain login
mail_debug = yes
auth_debug = yes
# verbose_ssl = yes
'';
#### OUTGOING MESSAGE REWRITING:
services.postfix.enableHeaderChecks = true;
@@ -191,4 +218,10 @@ in
# pattern = "/^Subject:.*activate your account/";
# }
];
sops.secrets."dovecot_passwd" = {
owner = config.users.users.dovecot2.name;
# TODO: debug why mail can't be sent without this being world-readable
mode = "0444";
};
}

View File

@@ -12,29 +12,12 @@ lib.mkIf false
sane.persist.sys.plaintext = [
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
];
sane.ports.ports."5222" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-client-to-server";
};
sane.ports.ports."5269" = {
protocol = [ "tcp" ];
visibleTo.wan = true;
description = "colin-xmpp-server-to-server";
};
sane.ports.ports."5280" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh";
};
sane.ports.ports."5281" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-prosody-https"; # necessary?
};
networking.firewall.allowedTCPPorts = [
5222 # XMPP client -> server
5269 # XMPP server -> server
5280 # bosh
5281 # Prosody HTTPS port (necessary?)
];
# provide access to certs
users.users.prosody.extraGroups = [ "nginx" ];

View File

@@ -27,7 +27,7 @@
# units in kBps
speed-limit-down = 3000;
speed-limit-down-enabled = true;
speed-limit-up = 600;
speed-limit-up = 300;
speed-limit-up-enabled = true;
# see: https://git.zknt.org/mirror/transmission/commit/cfce6e2e3a9b9d31a9dafedd0bdc8bf2cdb6e876?lang=bg-BG

View File

@@ -6,7 +6,7 @@
sane.services.trust-dns.listenAddrsIPv4 = [
# specify each address explicitly, instead of using "*".
# this ensures responses are sent from the address at which the request was received.
config.sane.hosts.by-name."servo".lan-ip
"192.168.0.5"
"10.0.1.5"
];
sane.services.trust-dns.quiet = true;
@@ -30,20 +30,17 @@
7d ; Expire
5m) ; Negative response TTL
'';
TXT."rev" = "2023052901";
CNAME."native" = "%CNAMENATIVE%";
A."@" = "%ANATIVE%";
A."wan" = "%AWAN%";
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
TXT."rev" = "2022122101";
# XXX NS records must also not be CNAME
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
A."ns1" = "%ANATIVE%";
A."ns1" = "%NATIVE%";
A."ns2" = "185.157.162.178";
A."ns3" = "185.157.162.178";
A."ovpns" = "185.157.162.178";
A."native" = "%NATIVE%";
A."@" = "%NATIVE%";
NS."@" = [
"ns1.uninsane.org."
"ns2.uninsane.org."
@@ -51,68 +48,20 @@
];
};
sane.services.trust-dns.zones."uninsane.org".file = "uninsane.org.zone";
sane.services.trust-dns.zonedir = null;
sane.services.trust-dns.zones."uninsane.org".file =
"/var/lib/trust-dns/uninsane.org.zone";
sane.services.trust-dns.package =
let
sed = "${pkgs.gnused}/bin/sed";
zone-dir = "/var/lib/trust-dns";
zone-wan = "${zone-dir}/wan/uninsane.org.zone";
zone-lan = "${zone-dir}/lan/uninsane.org.zone";
zone-template = config.sane.services.trust-dns.zones."uninsane.org".file;
in pkgs.writeShellScriptBin "named" ''
# compute wan/lan values
mkdir -p ${zone-dir}/{ovpn,wan,lan}
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
lan=${config.sane.hosts.by-name."servo".lan-ip}
# create specializations that resolve native.uninsane.org to different CNAMEs
${sed} s/%AWAN%/$wan/ ${zone-template} \
| ${sed} s/%CNAMENATIVE%/wan/ \
| ${sed} s/%ANATIVE%/$wan/ \
> ${zone-wan}
${sed} s/%AWAN%/$wan/ ${zone-template} \
| ${sed} s/%CNAMENATIVE%/servo.lan/ \
| ${sed} s/%ANATIVE%/$lan/ \
> ${zone-lan}
# launch the different interfaces, separately
${pkgs.trust-dns}/bin/named --port 53 --zonedir ${zone-dir}/wan/ $@ &
WANPID=$!
${pkgs.trust-dns}/bin/named --port 1053 --zonedir ${zone-dir}/lan/ $@ &
LANPID=$!
# wait until any of the processes exits, then kill them all and exit error
while kill -0 $WANPID $LANPID ; do
sleep 5
done
kill $WANPID $LANPID
exit 1
'';
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
networking.nat.enable = true;
networking.nat.extraCommands = ''
# redirect incoming DNS requests from LAN addresses
# to the LAN-specialized DNS service
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
# because they get cleanly reset across activations or `systemctl restart firewall`
# instead of accumulating cruft
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
-m iprange --src-range 10.78.76.0-10.78.79.255 \
-j DNAT --to-destination :1053
systemd.services.trust-dns.preStart = let
sed = "${pkgs.gnused}/bin/sed";
zone-dir = "/var/lib/trust-dns";
zone-out = "${zone-dir}/uninsane.org.zone";
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
in ''
# make WAN records available to trust-dns
mkdir -p ${zone-dir}
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
'';
sane.ports.ports."1053" = {
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
# TODO: try nixos-nat-post instead?
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace";
};
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
}

22
hosts/common/cross.nix Normal file
View File

@@ -0,0 +1,22 @@
{ config, ... }:
let
mkCrossFrom = localSystem: pkgs: import pkgs.path {
inherit localSystem;
crossSystem = pkgs.stdenv.hostPlatform.system;
inherit (config.nixpkgs) config overlays;
};
in
{
# the configuration of which specific package set `pkgs.cross` refers to happens elsewhere;
# here we just define them all.
nixpkgs.overlays = [
(next: prev: {
# non-emulated packages build *from* local *for* target.
# for large packages like the linux kernel which are expensive to build under emulation,
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
crossFrom."x86_64-linux" = mkCrossFrom "x86_64-linux" next;
crossFrom."aarch64-linux" = mkCrossFrom "aarch64-linux" next;
})
];
}

View File

@@ -1,16 +1,17 @@
{ lib, pkgs, ... }:
{ pkgs, ... }:
{
imports = [
./cross.nix
./feeds.nix
./fs.nix
./hardware.nix
./home
./i2p.nix
./ids.nix
./machine-id.nix
./net.nix
./nix-path
./persist.nix
./programs
./programs.nix
./secrets.nix
./ssh.nix
./users.nix
@@ -18,56 +19,39 @@
];
sane.nixcache.enable-trusted-keys = true;
sane.nixcache.enable = lib.mkDefault true;
sane.persist.enable = lib.mkDefault true;
sane.programs.sysadminUtils.enableFor.system = lib.mkDefault true;
sane.programs.consoleUtils.enableFor.user.colin = lib.mkDefault true;
sane.programs.sysadminUtils.enableFor.system = true;
sane.programs.consoleUtils.enableFor.user.colin = true;
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
sane.fs."/var/lib/private".dir.acl.mode = "0700";
nixpkgs.config.allowUnfree = true;
nixpkgs.config.allowBroken = true; # NIXPKGS_ALLOW_BROKEN
# time.timeZone = "America/Los_Angeles";
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
# allow `nix flake ...` command
# TODO: is this still required?
nix.extraOptions = ''
experimental-features = nix-command flakes
'';
# hardlinks identical files in the nix store to save 25-35% disk space.
# unclear _when_ this occurs. it's not a service.
# does the daemon continually scan the nix store?
# does the builder use some content-addressed db to efficiently dedupe?
nix.settings.auto-optimise-store = true;
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
nix.nixPath = [
"nixpkgs=${pkgs.path}"
"nixpkgs-overlays=${../..}/overlays"
];
fonts = {
enableDefaultFonts = true;
fonts = with pkgs; [ font-awesome noto-fonts-emoji hack-font ];
fonts = with pkgs; [ font-awesome twitter-color-emoji hack-font ];
fontconfig.enable = true;
fontconfig.defaultFonts = {
emoji = [ "Font Awesome 6 Free" "Noto Color Emoji" ];
emoji = [ "Font Awesome 6 Free" "Twitter Color Emoji" ];
monospace = [ "Hack" ];
serif = [ "DejaVu Serif" ];
sansSerif = [ "DejaVu Sans" ];
};
};
# XXX: twitter-color-emoji doesn't cross-compile; but not-fonts-emoji does
# fonts = {
# enableDefaultFonts = true;
# fonts = with pkgs; [ font-awesome twitter-color-emoji hack-font ];
# fontconfig.enable = true;
# fontconfig.defaultFonts = {
# emoji = [ "Font Awesome 6 Free" "Twitter Color Emoji" ];
# monospace = [ "Hack" ];
# serif = [ "DejaVu Serif" ];
# sansSerif = [ "DejaVu Sans" ];
# };
# };
# disable non-required packages like nano, perl, rsync, strace
environment.defaultPackages = [];

View File

@@ -1,9 +1,3 @@
# candidates:
# - The Nonlinear Library (podcast): <https://forum.effectivealtruism.org/posts/JTZTBienqWEAjGDRv/listen-to-more-ea-content-with-the-nonlinear-library>
# - has ~10 posts per day, text-to-speech; i would need better tagging before adding this
# - <https://www.metaculus.com/questions/11102/introducing-the-metaculus-journal-podcast/>
# - dead since 2022/10 - 2023/03
{ lib, sane-data, ... }:
let
hourly = { freq = "hourly"; };
@@ -56,29 +50,18 @@ let
(fromDb "lexfridman.com/podcast" // rat)
## Astral Codex Ten
(fromDb "sscpodcast.libsyn.com" // rat)
## Less Wrong Curated
(fromDb "feeds.libsyn.com/421877" // rat)
## Econ Talk
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
## Cory Doctorow -- both podcast & text entries
(fromDb "craphound.com" // pol)
## Maggie Killjoy -- referenced by Cory Doctorow
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol)
(fromDb "congressionaldish.libsyn.com" // pol)
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
## Civboot -- https://anchor.fm/civboot
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
## Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
(mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent)
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
## Daniel Huberman on sleep
(fromDb "feeds.megaphone.fm/hubermanlab" // uncat)
## Multidisciplinary Association for Psychedelic Studies
(fromDb "mapspodcast.libsyn.com" // uncat)
(fromDb "allinchamathjason.libsyn.com" // pol)
(fromDb "feeds.transistor.fm/acquired" // tech)
## ACQ2 - more "Acquired" episodes
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech)
(fromDb "acquired.libsyn.com" // tech)
# The Intercept - Deconstructed; also available: <rss.acast.com/deconstructed>
(fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol)
## The Daily
@@ -107,31 +90,20 @@ let
(fromDb "seattlenice.buzzsprout.com" // pol)
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
## UnNamed Reverse Engineering Podcast
(fromDb "reverseengineering.libsyn.com/rss" // tech)
## The Witch Trials of J.K. Rowling
## - <https://www.thefp.com/witchtrials>
(mkPod "https://feeds.megaphone.fm/RUNMED9919162779" // pol // infrequent)
];
texts = [
# AGGREGATORS (> 1 post/day)
(fromDb "lwn.net" // tech)
(fromDb "lesswrong.com" // rat)
# (fromDb "econlib.org" // pol)
(fromDb "econlib.org" // pol)
# AGGREGATORS (< 1 post/day)
(fromDb "palladiummag.com" // uncat)
(fromDb "profectusmag.com" // uncat)
(fromDb "semiaccurate.com" // tech)
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
(fromDb "tuxphones.com" // tech)
(fromDb "spectrum.ieee.org" // tech)
(fromDb "theregister.com" // tech)
(fromDb "thisweek.gnome.org" // tech)
# more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent)
(mkText "https://nixos.org/blog/stories-rss.xml" // tech // weekly)
## n.b.: 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)
@@ -139,12 +111,9 @@ let
(fromDb "rifters.com/crawl" // uncat)
# DEVELOPERS
(fromDb "blog.jmp.chat" // tech)
(fromDb "uninsane.org" // tech)
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
(fromDb "xn--gckvb8fzb.com" // tech)
(fromDb "mg.lol" // tech)
# (fromDb "drewdevault.com" // tech)
(fromDb "drewdevault.com" // tech)
## Ken Shirriff
(fromDb "righto.com" // tech)
## shared blog by a few NixOS devs, notably onny
@@ -162,10 +131,6 @@ let
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
(fromDb "jefftk.com" // tech)
(fromDb "pomeroyb.com" // tech)
(mkText "https://til.simonwillison.net/tils/feed.atom" // tech // weekly)
# TECH PROJECTS
(fromDb "blog.rust-lang.org" // tech)
# (TECH; POL) COMMENTATORS
## Matt Webb -- engineering-ish, but dreamy
@@ -182,8 +147,7 @@ let
(fromDb "lynalden.com" // pol)
(fromDb "austinvernon.site" // tech)
(mkSubstack "oversharing" // pol // daily)
(mkSubstack "byrnehobart" // pol // infrequent)
# (mkSubstack "doomberg" // tech // weekly) # articles are all pay-walled
(mkSubstack "doomberg" // tech // weekly)
## David Rosenthal
(fromDb "blog.dshr.org" // pol)
## Matt Levine
@@ -191,7 +155,6 @@ let
(fromDb "stpeter.im/atom.xml" // pol)
## Peter Saint-Andre -- side project of stpeter.im
(fromDb "philosopher.coach" // rat)
(fromDb "morningbrew.com/feed" // pol)
# RATIONALITY/PHILOSOPHY/ETC
(mkSubstack "samkriss" // humor // infrequent)
@@ -210,15 +173,10 @@ let
(fromDb "sideways-view.com" // rat)
## Sean Carroll
(fromDb "preposterousuniverse.com" // rat)
(mkSubstack "eliqian" // rat // weekly)
(mkText "https://acoup.blog/feed" // rat // weekly)
## mostly dating topics. not advice, or humor, but looking through a social lens
(fromDb "putanumonit.com" // rat)
# LOCAL
(fromDb "capitolhillseattle.com" // pol)
# CODE
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
];
@@ -228,7 +186,6 @@ let
(fromDb "xkcd.com" // img // humor)
(fromDb "pbfcomics.com" // img // humor)
# (mkImg "http://dilbert.com/feed" // humor // daily)
(fromDb "poorlydrawnlines.com/feed" // img // humor)
# ART
(fromDb "miniature-calendar.com" // img // art // daily)

View File

@@ -28,11 +28,6 @@
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
powerManagement.powertop.enable = false;
services.logind.extraConfig = ''
# dont shutdown when power button is short-pressed
HandlePowerKey=ignore
'';
# services.snapper.configs = {
# root = {
# subvolume = "/";

View File

@@ -0,0 +1,11 @@
# Terminal UI mail client
{ config, sane-lib, ... }:
{
sops.secrets."aerc_accounts" = {
owner = config.users.users.colin.name;
sopsFile = ../../../secrets/universal/aerc_accounts.conf;
format = "binary";
};
sane.user.fs.".config/aerc/accounts.conf" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.aerc_accounts.path;
}

View File

@@ -1,9 +1,23 @@
{ ... }:
{
imports = [
./aerc.nix
./firefox.nix
./gfeeds.nix
./git.nix
./gpodder.nix
./keyring.nix
./kitty.nix
./libreoffice.nix
./mime.nix
./mpv.nix
./neovim.nix
./newsflash.nix
./splatmoji.nix
./ssh.nix
./sublime-music.nix
./vlc.nix
./xdg-dirs.nix
./zsh
];
}

View File

@@ -0,0 +1,202 @@
# common settings to toggle (at runtime, in about:config):
# > security.ssl.require_safe_negotiation
# librewolf is a forked firefox which patches firefox to allow more things
# (like default search engines) to be configurable at runtime.
# many of the settings below won't have effect without those patches.
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
{ config, lib, pkgs, sane-lib, ...}:
with lib;
let
cfg = config.sane.web-browser;
# allow easy switching between firefox and librewolf with `defaultSettings`, below
librewolfSettings = {
browser = pkgs.librewolf-unwrapped;
# browser = pkgs.librewolf-unwrapped.overrideAttrs (drv: {
# # this allows side-loading unsigned addons
# MOZ_REQUIRE_SIGNING = false;
# });
libName = "librewolf";
dotDir = ".librewolf";
cacheDir = ".cache/librewolf"; # TODO: is it?
desktop = "librewolf.desktop";
};
firefoxSettings = {
browser = pkgs.firefox-esr-unwrapped;
libName = "firefox";
dotDir = ".mozilla/firefox";
cacheDir = ".cache/mozilla";
desktop = "firefox.desktop";
};
defaultSettings = firefoxSettings;
# defaultSettings = librewolfSettings;
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
inherit name hash;
url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
# extid can be found by unar'ing the above xpi, and copying browser_specific_settings.gecko.id field
fixedExtid = extid;
};
localAddon = pkg: pkgs.fetchFirefoxAddon {
inherit (pkg) name;
src = "${pkg}/share/mozilla/extensions/\\{ec8030f7-c20a-464f-9b0e-13a3a9e97384\\}/${pkg.extid}.xpi";
fixedExtid = pkg.extid;
};
package = pkgs.wrapFirefox cfg.browser.browser {
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
inherit (cfg.browser) libName;
extraNativeMessagingHosts = [ pkgs.browserpass ];
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
nixExtensions = concatMap (ext: optional ext.enable ext.package) (attrValues cfg.addons);
extraPolicies = {
NoDefaultBookmarks = true;
SearchEngines = {
Default = "DuckDuckGo";
};
AppUpdateURL = "https://localhost";
DisableAppUpdate = true;
OverrideFirstRunPage = "";
OverridePostUpdatePage = "";
DisableSystemAddonUpdate = true;
DisableFirefoxStudies = true;
DisableTelemetry = true;
DisableFeedbackCommands = true;
DisablePocket = true;
DisableSetDesktopBackground = false;
# remove many default search providers
# XXX this seems to prevent the `nixExtensions` from taking effect
# Extensions.Uninstall = [
# "google@search.mozilla.org"
# "bing@search.mozilla.org"
# "amazondotcom@search.mozilla.org"
# "ebay@search.mozilla.org"
# "twitter@search.mozilla.org"
# ];
# XXX doesn't seem to have any effect...
# docs: https://github.com/mozilla/policy-templates#homepage
# Homepage = {
# HomepageURL = "https://uninsane.org/";
# StartPage = "homepage";
# };
# NewTabPage = true;
};
};
addonOpts = types.submodule {
options = {
package = mkOption {
type = types.package;
};
enable = mkOption {
type = types.bool;
};
};
};
in
{
options = {
sane.web-browser.browser = mkOption {
default = defaultSettings;
type = types.attrs;
};
sane.web-browser.persistData = mkOption {
description = "optional store name to which persist browsing data (like history)";
type = types.nullOr types.str;
default = null;
};
sane.web-browser.persistCache = mkOption {
description = "optional store name to which persist browser cache";
type = types.nullOr types.str;
default = "cryptClearOnBoot";
};
sane.web-browser.addons = mkOption {
type = types.attrsOf addonOpts;
default = {
# get names from:
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
# browserpass-ce.package = addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=";
browserpass-extension.package = localAddon pkgs.browserpass-extension;
bypass-paywalls-clean.package = addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-oUwdqdAwV3DezaTtOMx7A/s4lzIws+t2f08mwk+325k=";
ether-metamask.package = addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=";
i2p-in-private-browsing.package = addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=";
sidebery.package = addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=";
sponsorblock.package = addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-hRsvLaAsVm3dALsTrJqHTNgRFAQcU7XSaGhr5G6+mFs=";
ublacklist.package = addon "ublacklist" "@ublacklist" "sha256-RqY5iHzbL2qizth7aguyOKWPyINXmrwOlf/OsfqAS48=";
ublock-origin.package = addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-a/ivUmY1P6teq9x0dt4CbgHt+3kBsEMMXlOfZ5Hx7cg=";
browserpass-extension.enable = lib.mkDefault true;
bypass-paywalls-clean.enable = lib.mkDefault true;
ether-metamask.enable = lib.mkDefault true;
i2p-in-private-browsing.enable = lib.mkDefault config.services.i2p.enable;
sidebery.enable = lib.mkDefault true;
sponsorblock.enable = lib.mkDefault true;
ublacklist.enable = lib.mkDefault true;
ublock-origin.enable = lib.mkDefault true;
};
};
};
config = {
sane.programs.web-browser = {
inherit package;
# TODO: define the persistence & fs config here
};
sane.programs.guiApps.suggestedPrograms = [ "web-browser" ];
# uBlock filter list configuration.
# specifically, enable the GDPR cookie prompt blocker.
# data.toOverwrite.filterLists is additive (i.e. it supplements the default filters)
# this configuration method is documented here:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# the specific attribute path is found via scraping ublock code here:
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
sane.user.fs."${cfg.browser.dotDir}/managed-storage/uBlock0@raymondhill.net.json" = sane-lib.fs.wantedText ''
{
"name": "uBlock0@raymondhill.net",
"description": "ignored",
"type": "storage",
"data": {
"toOverwrite": "{\"filterLists\": [\"fanboy-cookiemonster\"]}"
}
}
'';
sane.user.fs."${cfg.browser.dotDir}/${cfg.browser.libName}.overrides.cfg" = sane-lib.fs.wantedText ''
// if we can't query the revocation status of a SSL cert because the issuer is offline,
// treat it as unrevoked.
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
defaultPref("security.OCSP.require", false);
'';
# flush the cache to disk to avoid it taking up too much tmp
sane.user.persist.byPath."${cfg.browser.cacheDir}" = lib.mkIf (cfg.persistCache != null) {
store = cfg.persistCache;
};
sane.user.persist.byPath."${cfg.browser.dotDir}/default" = lib.mkIf (cfg.persistData != null) {
store = cfg.persistData;
};
sane.user.fs."${cfg.browser.dotDir}/default" = sane-lib.fs.wantedDir;
# instruct Firefox to put the profile in a predictable directory (so we can do things like persist just it).
# XXX: the directory *must* exist, even if empty; Firefox will not create the directory itself.
sane.user.fs."${cfg.browser.dotDir}/profiles.ini" = sane-lib.fs.wantedText ''
[Profile0]
Name=default
IsRelative=1
Path=default
Default=1
[General]
StartWithLastProfile=1
'';
};
}

View File

@@ -0,0 +1,42 @@
# gnome feeds RSS viewer
{ config, lib, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
in {
sane.user.fs.".config/org.gabmus.gfeeds.json" = sane-lib.fs.wantedText (
builtins.toJSON {
# feed format is a map from URL to a dict,
# with dict["tags"] a list of string tags.
feeds = sane-lib.mapToAttrs (feed: {
name = feed.url;
value.tags = [ feed.cat feed.freq ];
}) wanted-feeds;
dark_reader = false;
new_first = true;
# windowsize = {
# width = 350;
# height = 650;
# };
max_article_age_days = 90;
enable_js = false;
max_refresh_threads = 3;
# saved_items = {};
# read_items = [];
show_read_items = true;
full_article_title = true;
# views: "webview", "reader", "rsscont"
default_view = "rsscont";
open_links_externally = true;
full_feed_name = false;
refresh_on_startup = true;
tags = lib.unique (
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
);
open_youtube_externally = false;
media_player = "vlc"; # default: mpv
}
);
}

View File

@@ -1,26 +1,18 @@
{ lib, pkgs, ... }:
{ lib, pkgs, sane-lib, ... }:
let
mkCfg = lib.generators.toINI { };
in
{
sane.programs.git.fs.".config/git/config".symlink.text = mkCfg {
# top-level options documented:
# - <https://git-scm.com/docs/git-config#_variables>
sane.user.fs.".config/git/config" = sane-lib.fs.wantedText (mkCfg {
user.name = "Colin";
user.email = "colin@uninsane.org";
alias.co = "checkout";
# difftastic docs:
# - <https://difftastic.wilfred.me.uk/git.html>
diff.tool = "difftastic";
difftool.prompt = false;
"difftool \"difftastic\"".cmd = ''${pkgs.difftastic}/bin/difft "$LOCAL" "$REMOTE"'';
# now run `git difftool` to use difftastic git
# render dates as YYYY-MM-DD HH:MM:SS +TZ
log.date = "iso";
};
});
}

View File

@@ -0,0 +1,12 @@
# gnome feeds RSS viewer
{ config, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
in {
sane.user.fs.".config/gpodderFeeds.opml" = sane-lib.fs.wantedText (
feeds.feedsToOpml wanted-feeds
);
}

View File

@@ -1,17 +1,15 @@
{ ... }:
{ pkgs, sane-lib, ... }:
{
sane.programs.kitty.fs.".config/kitty/kitty.conf".symlink.text = ''
sane.user.fs.".config/kitty/kitty.conf" = sane-lib.fs.wantedText ''
# docs: https://sw.kovidgoyal.net/kitty/conf/
# disable terminal bell (when e.g. you backspace too many times)
enable_audio_bell no
map ctrl+n new_os_window_with_cwd
include ${./PaperColor_dark.conf}
include ${pkgs.kitty-themes}/themes/PaperColor_dark.conf
'';
# include ${pkgs.kitty-themes}/themes/PaperColor_dark.conf
# THEME CHOICES:
# docs: https://github.com/kovidgoyal/kitty-themes
# theme = "1984 Light"; # dislike: awful, harsh blues/teals

View File

@@ -1,8 +1,8 @@
{ ... }:
{ sane-lib, ... }:
{
# libreoffice: disable first-run stuff
sane.programs.libreoffice-fresh.fs.".config/libreoffice/4/user/registrymodifications.xcu".symlink.text = ''
sane.user.fs.".config/libreoffice/4/user/registrymodifications.xcu" = sane-lib.fs.wantedText ''
<?xml version="1.0" encoding="UTF-8"?>
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>

View File

@@ -1,7 +1,7 @@
{ config, sane-lib, ...}:
let
www = config.sane.programs.web-browser.config.browser.desktop;
www = config.sane.web-browser.browser.desktop;
pdf = "org.gnome.Evince.desktop";
md = "obsidian.desktop";
thumb = "org.gnome.gThumb.desktop";
@@ -28,7 +28,6 @@ in
# VIDEO
"video/mp4" = video;
"video/quicktime" = video;
"video/webm" = video;
"video/x-matroska" = video;
# HTML
"text/html" = www;

10
hosts/common/home/mpv.nix Normal file
View File

@@ -0,0 +1,10 @@
{ sane-lib, ... }:
{
# format is <key>=%<length>%<value>
sane.user.fs.".config/mpv/mpv.conf" = sane-lib.fs.wantedText ''
save-position-on-quit=%3%yes
keep-open=%3%yes
'';
}

View File

@@ -1,8 +1,8 @@
{ config, lib, pkgs, ... }:
{ lib, pkgs, ... }:
let
inherit (builtins) map;
inherit (lib) concatMapStrings mkIf optionalString;
inherit (lib) concatMapStrings optionalString;
# this structure roughly mirrors home-manager's `programs.neovim.plugins` option
plugins = with pkgs.vimPlugins; [
# docs: surround-nvim: https://github.com/ur4ltz/surround.nvim/
@@ -72,9 +72,9 @@ let
in
{
# private because there could be sensitive things in the swap
sane.programs.neovim.persist.private = [ ".cache/vim-swap" ];
sane.user.persist.private = [ ".cache/vim-swap" ];
programs.neovim = mkIf config.sane.programs.neovim.enabled {
programs.neovim = {
# neovim: https://github.com/neovim/neovim
enable = true;
viAlias = true;

View File

@@ -6,10 +6,7 @@ let
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
in {
sane.programs.newsflash = {
persist.plaintext = [ ".local/share/news-flash" ];
fs.".config/newsflashFeeds.opml".symlink.text =
feeds.feedsToOpml wanted-feeds
;
};
sane.user.fs.".config/newsflashFeeds.opml" = sane-lib.fs.wantedText (
feeds.feedsToOpml wanted-feeds
);
}

View File

@@ -0,0 +1,19 @@
# borrows from:
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
{ pkgs, sane-lib, ... }:
{
sane.user.persist.plaintext = [ ".local/state/splatmoji" ];
sane.user.fs.".config/splatmoji/splatmoji.config" = sane-lib.fs.wantedText ''
history_file=~/.local/state/splatmoji/history
history_length=5
# TODO: wayland equiv
paste_command=xdotool key ctrl+v
# rofi_command=${pkgs.wofi}/bin/wofi --dmenu --insensitive --cache-file /dev/null
rofi_command=${pkgs.fuzzel}/bin/fuzzel -d -i -w 60
xdotool_command=${pkgs.wtype}/bin/wtype
# TODO: wayland equiv
xsel_command=xsel -b -i
'';
}

View File

@@ -3,8 +3,7 @@
with lib;
let
host = config.networking.hostName;
user-pubkey-full = config.sane.ssh.pubkeys."colin@${host}" or {};
user-pubkey = user-pubkey-full.asUserKey or null;
user-pubkey = config.sane.ssh.pubkeys."colin@${host}".asUserKey;
host-keys = filter (k: k.user == "root") (attrValues config.sane.ssh.pubkeys);
known-hosts-text = concatStringsSep
"\n"
@@ -14,8 +13,7 @@ in
{
# ssh key is stored in private storage
sane.user.persist.private = [ ".ssh/id_ed25519" ];
sane.user.fs.".ssh/id_ed25519.pub" =
mkIf (user-pubkey != null) (sane-lib.fs.wantedText user-pubkey);
sane.user.fs.".ssh/id_ed25519.pub" = sane-lib.fs.wantedText user-pubkey;
sane.user.fs.".ssh/known_hosts" = sane-lib.fs.wantedText known-hosts-text;
users.users.colin.openssh.authorizedKeys.keys =

View File

@@ -0,0 +1,11 @@
{ config, sane-lib, ... }:
{
# TODO: this should only be shipped on gui platforms
sops.secrets."sublime_music_config" = {
owner = config.users.users.colin.name;
sopsFile = ../../../secrets/universal/sublime_music_config.json.bin;
format = "binary";
};
sane.user.fs.".config/sublime-music/config.json" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.sublime_music_config.path;
}

20
hosts/common/home/vlc.nix Normal file
View File

@@ -0,0 +1,20 @@
{ config, lib, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
podcast-urls = lib.concatStringsSep "|" (
builtins.map (feed: feed.url) wanted-feeds
);
in
{
sane.user.fs.".config/vlc/vlcrc" = sane-lib.fs.wantedText ''
[podcast]
podcast-urls=${podcast-urls}
[core]
metadata-network-access=0
[qt]
qt-privacy-ask=0
'';
}

View File

@@ -0,0 +1,143 @@
{ pkgs, sane-lib, ... }:
let
# powerlevel10k prompt config
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
p10k-overrides = ''
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
# i can disable gitstatusd and get slower fallback git queries:
# - either universally
# - or selectively by path
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
# show user@host also when logged into the current machine.
# default behavior is to show it only over ssh.
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
'';
prezto-init = ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source ${pkgs.zsh-prezto}/share/zsh-prezto/init.zsh
'';
in
{
sane.user.persist.plaintext = [
# we don't need to full zsh dir -- just the history file --
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
# TODO: should be private?
".local/share/zsh"
# cache gitstatus otherwise p10k fetched it from the net EVERY BOOT
".cache/gitstatus"
];
# zsh/prezto complains if zshrc doesn't exist; but it does allow an "empty" file.
sane.user.fs.".config/zsh/.zshrc" = sane-lib.fs.wantedText "# ";
# enable zsh completions
environment.pathsToLink = [ "/share/zsh" ];
programs.zsh = {
enable = true;
histFile = "$HOME/.local/share/zsh/history";
shellAliases = {
":q" = "exit";
# common typos
"cd.." = "cd ..";
"cd../" = "cd ../";
};
setOptions = [
# defaults:
"HIST_IGNORE_DUPS"
"SHARE_HISTORY"
"HIST_FCNTL_LOCK"
# disable `rm *` confirmations
"rmstarsilent"
];
# .zshenv config:
shellInit = ''
ZDOTDIR=$HOME/.config/zsh
'';
# .zshrc config:
interactiveShellInit =
(builtins.readFile ./p10k.zsh)
+ p10k-overrides
+ prezto-init
+ ''
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
autoload -Uz zmv
HISTORY_IGNORE='(sane-shutdown *|sane-reboot *|rm *)'
# extra aliases
# TODO: move to `shellAliases` config?
function nd() {
mkdir -p "$1";
pushd "$1";
}
# auto-cd into any of these dirs by typing them and pressing 'enter':
hash -d 3rd="/home/colin/dev/3rd"
hash -d dev="/home/colin/dev"
hash -d knowledge="/home/colin/knowledge"
hash -d nixos="/home/colin/nixos"
hash -d nixpkgs="/home/colin/dev/3rd/nixpkgs"
hash -d ref="/home/colin/ref"
hash -d secrets="/home/colin/knowledge/secrets"
hash -d tmp="/home/colin/tmp"
hash -d uninsane="/home/colin/dev/uninsane"
hash -d Videos="/home/colin/Videos"
'';
syntaxHighlighting.enable = true;
vteIntegration = true;
};
# enable a command-not-found hook to show nix packages that might provide the binary typed.
programs.nix-index.enable = true;
programs.command-not-found.enable = false; #< mutually exclusive with nix-index
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
# see: https://github.com/sorin-ionescu/prezto
# i believe this file is auto-sourced by the prezto init.zsh script.
sane.user.fs.".config/zsh/.zpreztorc" = sane-lib.fs.wantedText ''
zstyle ':prezto:*:*' color 'yes'
# modules (they ship with prezto):
# ENVIRONMENT: configures jobs to persist after shell exit; other basic niceties
# TERMINAL: auto-titles terminal (e.g. based on cwd)
# EDITOR: configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
# HISTORY: `history-stat` alias, setopts for good history defaults
# DIRECTORY: sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack
# SPECTRUM: helpers for term colors and styling. used by prompts? might be unnecessary
# UTILITY: configures aliases like `ll`, `la`, disables globbing for things like rsync
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
# COMPLETION: tab completion. requires `utility` module prior to loading
# TODO: enable AUTO_PARAM_SLASH
zstyle ':prezto:load' pmodule \
'environment' \
'terminal' \
'editor' \
'history' \
'directory' \
'spectrum' \
'utility' \
'completion' \
'prompt'
# default keymap. try also `vicmd` (vim normal mode, AKA "cmd mode") or `vi`.
zstyle ':prezto:module:editor' key-bindings 'emacs'
zstyle ':prezto:module:prompt' theme 'powerlevel10k'
# disable `mv` confirmation (and `rm`, too, unfortunately)
zstyle ':prezto:module:utility' safe-ops 'no'
'';
}

4
hosts/common/i2p.nix Normal file
View File

@@ -0,0 +1,4 @@
{ ... }:
{
# services.i2p.enable = true;
}

View File

@@ -1,6 +1,3 @@
# TODO: migrate to nixpkgs `config.ids.uids`
# - note that nixpkgs' `config.ids.uids` is strictly a database: it doesn't set anything by default
# whereas our impl sets the gid/uid of the user/group specified if they exist.
{ ... }:
{
@@ -15,8 +12,6 @@
sane.ids.acme.gid = 996;
sane.ids.pleroma.uid = 997;
sane.ids.acme.uid = 998;
sane.ids.matrix-appservice-irc.uid = 993;
sane.ids.matrix-appservice-irc.gid = 992;
# greetd (used by sway)
sane.ids.greeter.uid = 999;
@@ -32,14 +27,6 @@
sane.ids.mautrix-signal.gid = 2404;
sane.ids.navidrome.uid = 2405;
sane.ids.navidrome.gid = 2405;
sane.ids.calibre-web.uid = 2406;
sane.ids.calibre-web.gid = 2406;
sane.ids.komga.uid = 2407;
sane.ids.komga.gid = 2407;
sane.ids.lemmy.uid = 2408;
sane.ids.lemmy.gid = 2408;
sane.ids.pict-rs.uid = 2409;
sane.ids.pict-rs.gid = 2409;
sane.ids.colin.uid = 1000;
sane.ids.guest.uid = 1100;
@@ -48,12 +35,11 @@
sane.ids.sshd.uid = 2001; # 997
sane.ids.sshd.gid = 2001; # 997
sane.ids.polkituser.gid = 2002; # 998
sane.ids.systemd-coredump.gid = 2003; # 996 # 2023/02/12-2023/02/28: upstream temporarily specified this as 151
sane.ids.systemd-coredump.gid = 2003; # 996
sane.ids.nscd.uid = 2004;
sane.ids.nscd.gid = 2004;
sane.ids.systemd-oom.uid = 2005;
sane.ids.systemd-oom.gid = 2005;
sane.ids.wireshark.gid = 2006;
# found on graphical hosts
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy

View File

@@ -1,4 +1,4 @@
{ lib, ... }:
{ config, lib, pkgs, ... }:
{
# the default backend is "wpa_supplicant".
@@ -20,8 +20,4 @@
General.RoamThreshold = "-52"; # default -70
General.RoamThreshold5G = "-52"; # default -76
};
networking.firewall.allowedUDPPorts = [
1900 # to received UPnP advertisements. required by sane-ip-check-upnp
];
}

View File

@@ -1,10 +0,0 @@
{ pkgs, ... }:
{
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
nix.nixPath = [
"nixpkgs=${pkgs.path}"
# 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"
];
}

View File

@@ -1,4 +0,0 @@
# XXX: NIX_PATH=...:nixpkgs-overlays=... will import every overlay in the directory
# so we prefer to give it a directory with just this *one* overlay, otherwise it imports conflicting overlays
# and gets stuck in a loop until it OOMs
import ../../../../overlays/all.nix

329
hosts/common/programs.nix Normal file
View File

@@ -0,0 +1,329 @@
{ lib, pkgs, ... }:
let
inherit (builtins) attrNames concatLists;
inherit (lib) mapAttrs mapAttrsToList mkDefault mkMerge optional;
sysadminPkgs = {
inherit (pkgs // {
# XXX can't `inherit` a nested attr, so we move them to the toplevel
"cacert.unbundled" = pkgs.cacert.unbundled;
})
btrfs-progs
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
cryptsetup
dig
efibootmgr
fatresize
fd
file
gawk
git
gptfdisk
hdparm
htop
iftop
inetutils # for telnet
iotop
iptables
jq
killall
lsof
nano
netcat
nethogs
nmap
openssl
parted
pciutils
powertop
pstree
ripgrep
screen
smartmontools
socat
strace
tcpdump
tree
usbutils
wget
;
};
consolePkgs = {
inherit (pkgs)
backblaze-b2
# cdrtools # TODO: fix for cross compilation
dmidecode
duplicity
efivar
flashrom
# fwupd # TODO: fix for cross compilation
ghostscript # TODO: imagemagick wrapper should add gs to PATH
gnupg
gocryptfs
gopass
gopass-jsonapi
ifuse
imagemagick
ipfs
kitty # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
libimobiledevice
libsecret # for managing user keyrings
lm_sensors # for sensors-detect
lshw
ffmpeg
memtester
networkmanager
nixpkgs-review
# nixos-generators
# nettools
nmon
oathToolkit # for oathtool
# ponymix
pulsemixer
python3
rsync
# python3Packages.eyeD3 # music tagging
sane-scripts
sequoia
snapper
sops
sox
speedtest-cli
sqlite # to debug sqlite3 databases
ssh-to-age
sudo
# tageditor # music tagging
unar
visidata
w3m
wireguard-tools
# youtube-dl
yt-dlp
;
};
guiPkgs = {
inherit (pkgs // (with pkgs; {
# XXX can't `inherit` a nested attr, so we move them to the toplevel
# TODO: could use some "flatten attrs" helper instead
"gnome.cheese" = gnome.cheese;
"gnome.dconf-editor" = gnome.dconf-editor;
"gnome.file-roller" = gnome.file-roller;
"gnome.gnome-disk-utility" = gnome.gnome-disk-utility;
"gnome.gnome-maps" = gnome.gnome-maps;
"gnome.nautilus" = gnome.nautilus;
"gnome.gnome-system-monitor" = gnome.gnome-system-monitor;
"gnome.gnome-terminal" = gnome.gnome-terminal;
"gnome.gnome-weather" = gnome.gnome-weather;
"libsForQt5.plasmatube" = libsForQt5.plasmatube;
}))
aerc # email client
audacity
celluloid # mpv frontend
chromium
clinfo
dino
electrum
element-desktop
emote
evince # works on phosh
# { pkg = fluffychat-moby; dir = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
foliate # e-book reader
font-manager
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
# then reboot (so that libsecret daemon re-loads the keyring...?)
# { pkg = fractal-latest; private = [ ".local/share/fractal" ]; }
# { pkg = fractal-next; private = [ ".local/share/fractal" ]; }
gajim # XMPP client
gimp # broken on phosh
"gnome.cheese"
"gnome.dconf-editor"
gnome-feeds # RSS reader (with claimed mobile support)
"gnome.file-roller"
"gnome.gnome-disk-utility"
"gnome.gnome-maps" # works on phosh
"gnome.nautilus"
# gnome-podcasts
"gnome.gnome-system-monitor"
"gnome.gnome-terminal" # works on phosh
"gnome.gnome-weather"
gpodder-configured
gthumb
inkscape
kdenlive
kid3 # audio tagging
krita
libreoffice-fresh # XXX colin: maybe don't want this on mobile
lollypop
mpv
networkmanagerapplet
newsflash
nheko
obsidian
pavucontrol
# picard # music tagging
playerctl
"libsForQt5.plasmatube" # Youtube player
soundconverter
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
# possible to pass config as a CLI arg (sublime-music -c config.json)
# { pkg = sublime-music; dir = [ ".local/share/sublime-music" ]; }
sublime-music-mobile
tdesktop # broken on phosh
tokodon
vlc
# pleroma client (Electron). input is broken on phosh. TODO(2023/02/02): fix electron19 input (insecure)
# whalebird
xdg-utils # for xdg-open
xterm # broken on phosh
;
};
x86GuiPkgs = {
inherit (pkgs)
discord
# kaiteki # Pleroma client
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
# gpt2tc # XXX: unreliable mirror
# TODO(unpin): handbrake is broken on aarch64-linux 2023/01/29
handbrake
logseq
losslesscut-bin
makemkv
monero-gui
signal-desktop
spotify
tor-browser-bundle-bin
zecwallet-lite
;
};
# define -- but don't enable -- the packages in some attrset.
# use `mkDefault` for the package here so we can customize some of them further down this file
declarePkgs = pkgsAsAttrs: mapAttrs (_n: p: {
package = mkDefault p;
}) pkgsAsAttrs;
in
{
config = {
sane.programs = mkMerge [
(declarePkgs sysadminPkgs)
(declarePkgs consolePkgs)
(declarePkgs guiPkgs)
(declarePkgs x86GuiPkgs)
{
# link the various package sets into their own meta packages
sysadminUtils = {
package = null;
suggestedPrograms = attrNames sysadminPkgs;
};
consoleUtils = {
package = null;
suggestedPrograms = attrNames consolePkgs;
};
guiApps = {
package = null;
suggestedPrograms = (attrNames guiPkgs)
++ optional (pkgs.system == "x86_64-linux") "x86GuiApps";
};
x86GuiApps = {
package = null;
suggestedPrograms = attrNames x86GuiPkgs;
};
}
{
# nontrivial package definitions
imagemagick.package = pkgs.imagemagick.override {
ghostscriptSupport = true;
};
dino.private = [ ".local/share/dino" ];
# creds, but also 200 MB of node modules, etc
discord = {
package = pkgs.discord.override {
# XXX 2022-07-31: fix to allow links to open in default web-browser:
# https://github.com/NixOS/nixpkgs/issues/78961
nss = pkgs.nss_latest;
};
private = [ ".config/discord" ];
};
# creds/session keys, etc
element-desktop.private = [ ".config/Element" ];
# `emote` will show a first-run dialog based on what's in this directory.
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
emote.dir = [ ".local/share/Emote" ];
# XXX: we preserve the whole thing because if we only preserve gPodder/Downloads
# then startup is SLOW during feed import, and we might end up with zombie eps in the dl dir.
gpodder-configured.dir = [ "gPodder" ];
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
monero-gui.dir = [ ".bitmonero" ];
mpv.dir = [ ".config/mpv/watch_later" ];
# not strictly necessary, but allows caching articles; offline use, etc.
newsflash.dir = [ ".local/share/news-flash" ];
nheko.private = [
".config/nheko" # config file (including client token)
".cache/nheko" # media cache
".local/share/nheko" # per-account state database
];
# settings (electron app)
obsidian.dir = [ ".config/obsidian" ];
# creds, media
signal-desktop.private = [ ".config/Signal" ];
# creds, widevine .so download. TODO: could easily manage these statically.
spotify.dir = [ ".config/spotify" ];
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
# possible to pass config as a CLI arg (sublime-music -c config.json)
# { pkg = sublime-music; dir = [ ".local/share/sublime-music" ]; }
sublime-music-mobile.dir = [ ".local/share/sublime-music" ];
tdesktop.private = [ ".local/share/TelegramDesktop" ];
tokodon.private = [ ".cache/KDE/tokodon" ];
# hardenedMalloc solves a crash at startup
# TODO 2023/02/02: is this safe to remove yet?
tor-browser-bundle-bin.package = pkgs.tor-browser-bundle-bin.override {
useHardenedMalloc = false;
};
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
vlc.dir = [ ".config/vlc" ];
whalebird.private = [ ".config/Whalebird" ];
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
zecwallet-lite.private = [ ".zcash" ];
}
];
# 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

@@ -1,6 +0,0 @@
# Terminal UI mail client
{ ... }:
{
sane.programs.aerc.secrets.".config/aerc/accounts.conf" = ../../../secrets/common/aerc_accounts.conf.bin;
}

View File

@@ -1,422 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (builtins) attrNames concatLists;
inherit (lib) mapAttrs mapAttrsToList mkDefault mkIf mkMerge optional;
flattenedPkgs = pkgs // (with pkgs; {
# XXX can't `inherit` a nested attr, so we move them to the toplevel
"cacert.unbundled" = pkgs.cacert.unbundled;
"gnome.cheese" = gnome.cheese;
"gnome.dconf-editor" = gnome.dconf-editor;
"gnome.file-roller" = gnome.file-roller;
"gnome.gnome-disk-utility" = gnome.gnome-disk-utility;
"gnome.gnome-maps" = gnome.gnome-maps;
"gnome.nautilus" = gnome.nautilus;
"gnome.gnome-system-monitor" = gnome.gnome-system-monitor;
"gnome.gnome-terminal" = gnome.gnome-terminal;
"gnome.gnome-weather" = gnome.gnome-weather;
"gnome.totem" = gnome.totem;
"libsForQt5.plasmatube" = libsForQt5.plasmatube;
});
sysadminPkgs = {
inherit (flattenedPkgs)
btrfs-progs
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
cryptsetup
dig
efibootmgr
fatresize
fd
file
gawk
git
gptfdisk
hdparm
htop
iftop
inetutils # for telnet
iotop
iptables
jq
killall
lsof
miniupnpc
nano
netcat
nethogs
nmap
openssl
parted
pciutils
powertop
pstree
ripgrep
screen
smartmontools
socat
strace
subversion
tcpdump
tree
usbutils
wget
wirelesstools # iwlist
;
};
sysadminExtraPkgs = {
# application-specific packages
inherit (pkgs)
backblaze-b2
duplicity
sqlite # to debug sqlite3 databases
;
};
iphonePkgs = {
inherit (pkgs)
ifuse
ipfs
libimobiledevice
;
};
tuiPkgs = {
inherit (pkgs)
aerc # email client
offlineimap # email mailox sync
visidata # TUI spreadsheet viewer/editor
w3m
;
};
# TODO: split these into smaller groups.
# - transcoders (ffmpeg, imagemagick) only wanted on desko/lappy ("powerutils"?)
consolePkgs = {
inherit (pkgs)
alsaUtils # for aplay, speaker-test
cdrtools
dmidecode
efivar
flashrom
fwupd
gh # MS GitHub cli
git # needed as a user package, for config.
gnupg
gocryptfs
gopass # TODO: shouldn't be needed here
gopass-jsonapi
imagemagick
kitty # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
libsecret # for managing user keyrings
lm_sensors # for sensors-detect
lshw
ffmpeg
# memtester
neovim
# nettools
# networkmanager
nixpkgs-review
# nixos-generators
nmon
# node2nix
# oathToolkit # for oathtool
# ponymix
pulsemixer
python3
ripgrep # needed as a user package, for config.
rsync
# python3Packages.eyeD3 # music tagging
sane-scripts
sequoia
snapper
sops
sox
speedtest-cli
# ssh-to-age
sudo
# tageditor # music tagging
unar
wireguard-tools
xdg-utils # for xdg-open
# yarn
# youtube-dl
yt-dlp
zsh
;
};
guiPkgs = {
inherit (flattenedPkgs)
# celluloid # mpv frontend
clinfo
emote
evince # works on phosh
# { pkg = fluffychat-moby; persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
# foliate # e-book reader
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
# then reboot (so that libsecret daemon re-loads the keyring...?)
# { pkg = fractal-latest; persist.private = [ ".local/share/fractal" ]; }
# { pkg = fractal-next; persist.private = [ ".local/share/fractal" ]; }
# "gnome.cheese"
"gnome.dconf-editor"
# gnome-feeds # RSS reader (with claimed mobile support)
"gnome.file-roller"
# "gnome.gnome-maps" # works on phosh
"gnome.nautilus"
# gnome-podcasts
"gnome.gnome-system-monitor"
# "gnome.gnome-terminal" # works on phosh
# "gnome.gnome-weather"
gpodder
gthumb
jellyfin-media-player
# lollypop
# mpv
networkmanagerapplet
# newsflash
nheko
pavucontrol
# picard # music tagging
playerctl
# "libsForQt5.plasmatube" # Youtube player
soundconverter
sublime-music
# tdesktop # broken on phosh
# tokodon
vlc
# pleroma client (Electron). input is broken on phosh. TODO(2023/02/02): fix electron19 input (insecure)
# whalebird
xterm # broken on phosh
;
};
desktopGuiPkgs = {
inherit (flattenedPkgs)
audacity
brave # for the integrated wallet -- as a backup
chromium
dino
electrum
element-desktop
font-manager
gajim # XMPP client
gimp # broken on phosh
"gnome.gnome-disk-utility"
# "gnome.totem" # video player, supposedly supports UPnP
handbrake
hase
inkscape
kdenlive
kid3 # audio tagging
krita
libreoffice-fresh
mumble
obsidian
slic3r
steam
wireshark # could maybe ship the cli as sysadmin pkg
;
};
x86GuiPkgs = {
inherit (pkgs)
discord
# kaiteki # Pleroma client
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
# gpt2tc # XXX: unreliable mirror
# logseq # Personal Knowledge Management
losslesscut-bin
makemkv
monero-gui
signal-desktop
spotify
tor-browser-bundle-bin
zecwallet-lite
;
};
# packages not part of any package set; not enabled by default
otherPkgs = {
inherit (pkgs)
lemmy-server
mx-sanebot
stepmania
;
};
# define -- but don't enable -- the packages in some attrset.
declarePkgs = pkgsAsAttrs: mapAttrs (_n: p: {
# no need to actually define the package here: it's defaulted
# package = mkDefault p;
}) pkgsAsAttrs;
in
{
imports = [
./aerc.nix
./git.nix
./gnome-feeds.nix
./gpodder.nix
./jellyfin-media-player.nix
./kitty
./libreoffice.nix
./mpv.nix
./neovim.nix
./newsflash.nix
./offlineimap.nix
./ripgrep.nix
./splatmoji.nix
./sublime-music.nix
./vlc.nix
./web-browser.nix
./wireshark.nix
./zeal.nix
./zsh
];
config = {
sane.programs = mkMerge [
(declarePkgs consolePkgs)
(declarePkgs desktopGuiPkgs)
(declarePkgs guiPkgs)
(declarePkgs iphonePkgs)
(declarePkgs sysadminPkgs)
(declarePkgs sysadminExtraPkgs)
(declarePkgs tuiPkgs)
(declarePkgs x86GuiPkgs)
(declarePkgs otherPkgs)
{
# link the various package sets into their own meta packages
consoleUtils = {
package = null;
suggestedPrograms = attrNames consolePkgs;
};
desktopGuiApps = {
package = null;
suggestedPrograms = attrNames desktopGuiPkgs;
};
guiApps = {
package = null;
suggestedPrograms = (attrNames guiPkgs)
++ [ "web-browser" ]
++ [ "tuiApps" ]
++ optional (pkgs.system == "x86_64-linux") "x86GuiApps";
};
iphoneUtils = {
package = null;
suggestedPrograms = attrNames iphonePkgs;
};
sysadminUtils = {
package = null;
suggestedPrograms = attrNames sysadminPkgs;
};
sysadminExtraUtils = {
package = null;
suggestedPrograms = attrNames sysadminExtraPkgs;
};
tuiApps = {
package = null;
suggestedPrograms = attrNames tuiPkgs;
};
x86GuiApps = {
package = null;
suggestedPrograms = attrNames x86GuiPkgs;
};
}
{
# nontrivial package definitions
dino.persist.private = [ ".local/share/dino" ];
# creds, but also 200 MB of node modules, etc
discord.persist.private = [ ".config/discord" ];
# creds/session keys, etc
element-desktop.persist.private = [ ".config/Element" ];
# `emote` will show a first-run dialog based on what's in this directory.
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
emote.persist.plaintext = [ ".local/share/Emote" ];
# MS GitHub stores auth token in .config
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
gh.persist.private = [ ".config/gh" ];
ghostscript = {}; # used by imagemagick
imagemagick = {
package = pkgs.imagemagick.override {
ghostscriptSupport = true;
};
suggestedPrograms = [ "ghostscript" ];
};
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
monero-gui.persist.plaintext = [ ".bitmonero" ];
mumble.persist.private = [ ".local/share/Mumble" ];
# not strictly necessary, but allows caching articles; offline use, etc.
nheko.persist.private = [
".config/nheko" # config file (including client token)
".cache/nheko" # media cache
".local/share/nheko" # per-account state database
];
# settings (electron app)
obsidian.persist.plaintext = [ ".config/obsidian" ];
# creds, media
signal-desktop.persist.private = [ ".config/Signal" ];
# printer/filament settings
slic3r.persist.plaintext = [ ".Slic3r" ];
# creds, widevine .so download. TODO: could easily manage these statically.
spotify.persist.plaintext = [ ".config/spotify" ];
steam.persist.plaintext = [
".steam"
".local/share/Steam"
];
tdesktop.persist.private = [ ".local/share/TelegramDesktop" ];
tokodon.persist.private = [ ".cache/KDE/tokodon" ];
# hardenedMalloc solves a crash at startup
# TODO 2023/02/02: is this safe to remove yet?
tor-browser-bundle-bin.package = pkgs.tor-browser-bundle-bin.override {
useHardenedMalloc = false;
};
whalebird.persist.private = [ ".config/Whalebird" ];
yarn.persist.plaintext = [ ".cache/yarn" ];
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
zecwallet-lite.persist.private = [ ".zcash" ];
}
];
# XXX: this might not be necessary. try removing this and cacert.unbundled (servo)?
environment.etc."ssl/certs".source = "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
# steam requires system-level config for e.g. firewall or controller support
programs.steam = mkIf config.sane.programs.steam.enabled {
enable = true;
# not sure if needed: stole this whole snippet from the wiki
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
};
};
}

View File

@@ -1,40 +0,0 @@
# gnome feeds RSS viewer
{ config, lib, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
in {
sane.programs.gnome-feeds.fs.".config/org.gabmus.gfeeds.json".symlink.text = builtins.toJSON {
# feed format is a map from URL to a dict,
# with dict["tags"] a list of string tags.
feeds = sane-lib.mapToAttrs (feed: {
name = feed.url;
value.tags = [ feed.cat feed.freq ];
}) wanted-feeds;
dark_reader = false;
new_first = true;
# windowsize = {
# width = 350;
# height = 650;
# };
max_article_age_days = 90;
enable_js = false;
max_refresh_threads = 3;
# saved_items = {};
# read_items = [];
show_read_items = true;
full_article_title = true;
# views: "webview", "reader", "rsscont"
default_view = "rsscont";
open_links_externally = true;
full_feed_name = false;
refresh_on_startup = true;
tags = lib.unique (
(builtins.catAttrs "cat" wanted-feeds) ++ (builtins.catAttrs "freq" wanted-feeds)
);
open_youtube_externally = false;
media_player = "vlc"; # default: mpv
};
}

View File

@@ -1,17 +0,0 @@
# gnome feeds RSS viewer
{ config, pkgs, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
in {
sane.programs.gpodder = {
package = pkgs.gpodder-configured;
fs.".config/gpodderFeeds.opml".symlink.text = feeds.feedsToOpml wanted-feeds;
# XXX: we preserve the whole thing because if we only preserve gPodder/Downloads
# then startup is SLOW during feed import, and we might end up with zombie eps in the dl dir.
persist.plaintext = [ "gPodder" ];
};
}

View File

@@ -1,13 +0,0 @@
{ pkgs, ... }:
{
sane.programs.jellyfin-media-player = {
package = pkgs.jellyfin-media-player;
# package = pkgs.jellyfin-media-player-qt6;
# jellyfin stores things in a bunch of directories: this one persists auth info.
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
# be fragile and take an hour+ to figure out.
persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
};
}

View File

@@ -1,47 +0,0 @@
# vim:ft=kitty
## name: PaperColor Dark
## author: Nikyle Nguyen
## license: MIT
## blurb: Dark color scheme inspired by Google's Material Design
# special
foreground #d0d0d0
background #1c1c1c
cursor #d0d0d0
cursor_text_color background
# black
color0 #1c1c1c
color8 #585858
# red
color1 #af005f
color9 #5faf5f
# green
# "color2" is the green color used by ls to indicate executability
# both as text color
# or as bg color when the text is blue (color4)
color2 #246a28
color10 #2df200
# yellow
color3 #d7af5f
color11 #af87d7
# blue
color4 #78c6ef
color12 #ffaf00
# magenta
color5 #808080
color13 #ff5faf
# cyan
color6 #d7875f
color14 #00afaf
# white
color7 #d0d0d0
color15 #5f8787

View File

@@ -1,13 +0,0 @@
{ ... }:
{
sane.programs.mpv = {
persist.plaintext = [ ".config/mpv/watch_later" ];
# format is <key>=%<length>%<value>
fs.".config/mpv/mpv.conf".symlink.text = ''
save-position-on-quit=%3%yes
keep-open=%3%yes
'';
};
}

View File

@@ -1,12 +0,0 @@
# mail archiving/synchronization tool.
#
# manually download all emails for an account with
# - `offlineimap -a <accountname>`
#
# view account names inside the secrets file, listed below.
{ ... }:
{
sane.programs.offlineimap.secrets.".config/offlineimap/config" = ../../../secrets/common/offlineimaprc.bin;
}

View File

@@ -1,9 +0,0 @@
{ ... }:
{
# .ignore file is read by ripgrep (rg), silver searcher (ag), maybe others.
# ignore translation files by default when searching, as they tend to have
# a LOT of duplicate text.
sane.programs.ripgrep.fs.".ignore".symlink.text = ''
po/
'';
}

View File

@@ -1,22 +0,0 @@
# borrows from:
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
{ pkgs, ... }:
{
sane.programs.splatmoji = {
persist.plaintext = [ ".local/state/splatmoji" ];
fs.".config/splatmoji/splatmoji.config".symlink.text = ''
# XXX doesn't seem to understand ~ as shorthand for `$HOME`
history_file=/home/colin/.local/state/splatmoji/history
history_length=5
# TODO: wayland equiv
paste_command=xdotool key ctrl+v
# rofi_command=${pkgs.wofi}/bin/wofi --dmenu --insensitive --cache-file /dev/null
rofi_command=${pkgs.fuzzel}/bin/fuzzel -d -i -w 60
xdotool_command=${pkgs.wtype}/bin/wtype
# TODO: wayland equiv
xsel_command=xsel -b -i
'';
};
}

View File

@@ -1,14 +0,0 @@
{ pkgs, ... }:
{
sane.programs.sublime-music = {
package = pkgs.sublime-music-mobile;
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
# possible to pass config as a CLI arg (sublime-music -c config.json)
persist.plaintext = [ ".local/share/sublime-music" ];
secrets.".config/sublime-music/config.json" = ../../../secrets/common/sublime_music_config.json.bin;
};
}

View File

@@ -1,24 +0,0 @@
{ config, lib, sane-lib, ... }:
let
feeds = sane-lib.feeds;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["podcast"] all-feeds;
podcast-urls = lib.concatStringsSep "|" (
builtins.map (feed: feed.url) wanted-feeds
);
in
{
sane.programs.vlc = {
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
persist.plaintext = [ ".config/vlc" ];
fs.".config/vlc/vlcrc".symlink.text = ''
[podcast]
podcast-urls=${podcast-urls}
[core]
metadata-network-access=0
[qt]
qt-privacy-ask=0
'';
};
}

View File

@@ -1,246 +0,0 @@
# common settings to toggle (at runtime, in about:config):
# > security.ssl.require_safe_negotiation
# librewolf is a forked firefox which patches firefox to allow more things
# (like default search engines) to be configurable at runtime.
# many of the settings below won't have effect without those patches.
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.sane.programs.web-browser.config;
# allow easy switching between firefox and librewolf with `defaultSettings`, below
librewolfSettings = {
browser = pkgs.librewolf-unwrapped;
# browser = pkgs.librewolf-unwrapped.overrideAttrs (drv: {
# # this allows side-loading unsigned addons
# MOZ_REQUIRE_SIGNING = false;
# });
libName = "librewolf";
dotDir = ".librewolf";
cacheDir = ".cache/librewolf"; # TODO: is it?
desktop = "librewolf.desktop";
};
firefoxSettings = {
browser = pkgs.firefox-esr-unwrapped;
libName = "firefox";
dotDir = ".mozilla/firefox";
cacheDir = ".cache/mozilla";
desktop = "firefox.desktop";
};
# defaultSettings = firefoxSettings;
defaultSettings = librewolfSettings;
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
inherit name hash;
url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
# extid can be found by unar'ing the above xpi, and copying browser_specific_settings.gecko.id field
fixedExtid = extid;
};
localAddon = pkg: pkgs.fetchFirefoxAddon {
inherit (pkg) name;
src = "${pkg}/share/mozilla/extensions/\\{ec8030f7-c20a-464f-9b0e-13a3a9e97384\\}/${pkg.extid}.xpi";
fixedExtid = pkg.extid;
};
package = pkgs.wrapFirefox cfg.browser.browser {
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
inherit (cfg.browser) libName;
extraNativeMessagingHosts = optional cfg.addons.browserpass-extension.enable pkgs.browserpass;
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
nixExtensions = concatMap (ext: optional ext.enable ext.package) (attrValues cfg.addons);
extraPolicies = {
FirefoxHome = {
Search = true;
Pocket = false;
Snippets = false;
TopSites = false;
Highlights = false;
};
NoDefaultBookmarks = true;
OfferToSaveLogins = false;
OfferToSaveLoginsDefault = false;
PasswordManagerEnabled = false;
SearchEngines = {
Default = "DuckDuckGo";
};
UserMessaging = {
ExtensionRecommendations = false;
SkipOnboarding = true;
};
# these were taken from Librewolf
AppUpdateURL = "https://localhost";
DisableAppUpdate = true;
OverrideFirstRunPage = "";
OverridePostUpdatePage = "";
DisableSystemAddonUpdate = true;
DisableFirefoxStudies = true;
DisableTelemetry = true;
DisableFeedbackCommands = true;
DisablePocket = true;
DisableSetDesktopBackground = false;
# remove many default search providers
# XXX this seems to prevent the `nixExtensions` from taking effect
# Extensions.Uninstall = [
# "google@search.mozilla.org"
# "bing@search.mozilla.org"
# "amazondotcom@search.mozilla.org"
# "ebay@search.mozilla.org"
# "twitter@search.mozilla.org"
# ];
# XXX doesn't seem to have any effect...
# docs: https://github.com/mozilla/policy-templates#homepage
# Homepage = {
# HomepageURL = "https://uninsane.org/";
# StartPage = "homepage";
# };
# NewTabPage = true;
};
# extraPrefs = ...
};
addonOpts = types.submodule {
options = {
package = mkOption {
type = types.package;
};
enable = mkOption {
type = types.bool;
};
};
};
configOpts = {
options = {
browser = mkOption {
default = defaultSettings;
type = types.anything;
};
persistData = mkOption {
description = "optional store name to which persist browsing data (like history)";
type = types.nullOr types.str;
default = null;
};
persistCache = mkOption {
description = "optional store name to which persist browser cache";
type = types.nullOr types.str;
default = "cryptClearOnBoot";
};
addons = mkOption {
type = types.attrsOf addonOpts;
default = {};
};
};
};
in
{
config = mkMerge [
({
sane.programs.web-browser.configOption = mkOption {
type = types.submodule configOpts;
default = {};
};
sane.programs.web-browser.config.addons = {
# get names from:
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
browserpass-extension = {
# package = addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=";
package = localAddon pkgs.browserpass-extension;
enable = lib.mkDefault true;
};
# TODO: build bypass-paywalls from source? it's mysteriously disappeared from the Mozilla store.
# bypass-paywalls-clean.package = addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-oUwdqdAwV3DezaTtOMx7A/s4lzIws+t2f08mwk+324k=";
# bypass-paywalls-clean.enable = lib.mkDefault true;
ether-metamask = {
package = addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=";
enable = lib.mkDefault true;
};
i2p-in-private-browsing = {
package = addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=";
enable = lib.mkDefault config.services.i2p.enable;
};
sidebery = {
package = addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=";
enable = lib.mkDefault true;
};
sponsorblock = {
package = addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-hRsvLaAsVm3dALsTrJqHTNgRFAQcU7XSaGhr5G6+mFs=";
enable = lib.mkDefault true;
};
ublacklist = {
package = addon "ublacklist" "@ublacklist" "sha256-RqY5iHzbL2qizth7aguyOKWPyINXmrwOlf/OsfqAS48=";
enable = lib.mkDefault true;
};
ublock-origin = {
package = addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-eHlQrU/b9X/6sTbHBpGAd+0VsLT7IrVCnd0AQ948lyA=";
enable = lib.mkDefault true;
};
};
})
({
sane.programs.web-browser = {
inherit package;
# uBlock filter list configuration.
# specifically, enable the GDPR cookie prompt blocker.
# data.toOverwrite.filterLists is additive (i.e. it supplements the default filters)
# this configuration method is documented here:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# the specific attribute path is found via scraping ublock code here:
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
fs."${cfg.browser.dotDir}/managed-storage/uBlock0@raymondhill.net.json".symlink.text = ''
{
"name": "uBlock0@raymondhill.net",
"description": "ignored",
"type": "storage",
"data": {
"toOverwrite": "{\"filterLists\": [\"fanboy-cookiemonster\"]}"
}
}
'';
fs."${cfg.browser.dotDir}/${cfg.browser.libName}.overrides.cfg".symlink.text = ''
// if we can't query the revocation status of a SSL cert because the issuer is offline,
// treat it as unrevoked.
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
defaultPref("security.OCSP.require", false);
'';
fs."${cfg.browser.dotDir}/default".dir = {};
# instruct Firefox to put the profile in a predictable directory (so we can do things like persist just it).
# XXX: the directory *must* exist, even if empty; Firefox will not create the directory itself.
fs."${cfg.browser.dotDir}/profiles.ini".symlink.text = ''
[Profile0]
Name=default
IsRelative=1
Path=default
Default=1
[General]
StartWithLastProfile=1
'';
};
})
(mkIf config.sane.programs.web-browser.enabled {
# TODO: move the persistence into the sane.programs API (above)
# flush the cache to disk to avoid it taking up too much tmp
sane.user.persist.byPath."${cfg.browser.cacheDir}" = lib.mkIf (cfg.persistCache != null) {
store = cfg.persistCache;
};
sane.user.persist.byPath."${cfg.browser.dotDir}/default" = lib.mkIf (cfg.persistData != null) {
store = cfg.persistData;
};
})
];
}

View File

@@ -1,5 +0,0 @@
{ config, ... }:
{
sane.programs.wireshark = {};
programs.wireshark.enable = config.sane.programs.wireshark.enabled;
}

View File

@@ -1,55 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (builtins) map;
inherit (lib) mkIf mkOption optionalString types;
cfg = config.sane.programs.docsets.config;
configOpts = types.submodule {
options = {
rustPkgs = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
};
in {
sane.programs.zeal = {
package = pkgs.zeal-qt5;
persist.plaintext = [
".cache/Zeal"
".local/share/Zeal"
];
fs.".local/share/Zeal/Zeal/docsets/system".symlink.target = "/run/current-system/sw/share/docset";
suggestedPrograms = [ "docsets" ];
};
sane.programs.docsets = {
configOption = mkOption {
type = configOpts;
default = {};
};
package = pkgs.symlinkJoin {
name = "docsets";
# build each package with rust docs
paths = map (name:
let
orig = pkgs."${name}";
withDocs = orig.overrideAttrs (upstream: {
nativeBuildInputs = upstream.nativeBuildInputs or [] ++ [
pkgs.cargoDocsetHook
];
});
in
"${toString withDocs}/share/docset"
) cfg.rustPkgs;
# link only the docs (not any binaries)
postBuild = optionalString (cfg.rustPkgs != []) ''
mkdir -p $out/share/docset
mv $out/*.docset $out/share/docset
'';
};
};
environment.pathsToLink = mkIf config.sane.programs.zeal.enabled [
"/share/docset"
];
}

View File

@@ -1,166 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkIf mkMerge mkOption types;
cfg = config.sane.zsh;
# powerlevel10k prompt config
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
p10k-overrides = ''
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
# i can disable gitstatusd and get slower fallback git queries:
# - either universally
# - or selectively by path
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
# show user@host also when logged into the current machine.
# default behavior is to show it only over ssh.
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
'';
prezto-init = ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source ${pkgs.zsh-prezto}/share/zsh-prezto/init.zsh
'';
in
{
options = {
sane.zsh = {
showDeadlines = mkOption {
type = types.bool;
default = true;
description = "show upcoming deadlines (frommy PKM) upon shell init";
};
};
};
config = mkMerge [
({
sane.programs.zsh = {
persist.plaintext = [
# we don't need to full zsh dir -- just the history file --
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
# TODO: should be private?
".local/share/zsh"
# cache gitstatus otherwise p10k fetched it from the net EVERY BOOT
".cache/gitstatus"
];
# zsh/prezto complains if zshrc doesn't exist; but it does allow an "empty" file.
fs.".config/zsh/.zshrc".symlink.text = "# ";
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
# see: https://github.com/sorin-ionescu/prezto
# i believe this file is auto-sourced by the prezto init.zsh script.
fs.".config/zsh/.zpreztorc".symlink.text = ''
zstyle ':prezto:*:*' color 'yes'
# modules (they ship with prezto):
# ENVIRONMENT: configures jobs to persist after shell exit; other basic niceties
# TERMINAL: auto-titles terminal (e.g. based on cwd)
# EDITOR: configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
# HISTORY: `history-stat` alias, setopts for good history defaults
# DIRECTORY: sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack
# SPECTRUM: helpers for term colors and styling. used by prompts? might be unnecessary
# UTILITY: configures aliases like `ll`, `la`, disables globbing for things like rsync
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
# COMPLETION: tab completion. requires `utility` module prior to loading
# TODO: enable AUTO_PARAM_SLASH
zstyle ':prezto:load' pmodule \
'environment' \
'terminal' \
'editor' \
'history' \
'directory' \
'spectrum' \
'utility' \
'completion' \
'prompt'
# default keymap. try also `vicmd` (vim normal mode, AKA "cmd mode") or `vi`.
zstyle ':prezto:module:editor' key-bindings 'emacs'
zstyle ':prezto:module:prompt' theme 'powerlevel10k'
# disable `mv` confirmation (and `rm`, too, unfortunately)
zstyle ':prezto:module:utility' safe-ops 'no'
'';
};
})
(mkIf config.sane.programs.zsh.enabled {
# enable zsh completions
environment.pathsToLink = [ "/share/zsh" ];
programs.zsh = {
enable = true;
histFile = "$HOME/.local/share/zsh/history";
shellAliases = {
":q" = "exit";
# common typos
"cd.." = "cd ..";
"cd../" = "cd ../";
};
setOptions = [
# defaults:
"HIST_IGNORE_DUPS"
"SHARE_HISTORY"
"HIST_FCNTL_LOCK"
# disable `rm *` confirmations
"rmstarsilent"
];
# .zshenv config:
shellInit = ''
ZDOTDIR=$HOME/.config/zsh
'';
# .zshrc config:
interactiveShellInit =
(builtins.readFile ./p10k.zsh)
+ p10k-overrides
+ prezto-init
+ ''
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
autoload -Uz zmv
HISTORY_IGNORE='(sane-shutdown *|sane-reboot *|rm *|nixos-rebuild.* switch)'
# extra aliases
# TODO: move to `shellAliases` config?
function nd() {
mkdir -p "$1";
pushd "$1";
}
''
+ lib.optionalString cfg.showDeadlines ''
${pkgs.sane-scripts.deadlines}/bin/sane-deadlines
''
+ ''
# auto-cd into any of these dirs by typing them and pressing 'enter':
hash -d 3rd="/home/colin/dev/3rd"
hash -d dev="/home/colin/dev"
hash -d knowledge="/home/colin/knowledge"
hash -d nixos="/home/colin/nixos"
hash -d nixpkgs="/home/colin/dev/3rd/nixpkgs"
hash -d ref="/home/colin/ref"
hash -d secrets="/home/colin/knowledge/secrets"
hash -d tmp="/home/colin/tmp"
hash -d uninsane="/home/colin/dev/uninsane"
hash -d Videos="/home/colin/Videos"
'';
syntaxHighlighting.enable = true;
vteIntegration = true;
};
# enable a command-not-found hook to show nix packages that might provide the binary typed.
programs.nix-index.enable = true;
programs.command-not-found.enable = false; #< mutually exclusive with nix-index
})
];
}

View File

@@ -1,49 +1,38 @@
# SOPS configuration:
# docs: https://github.com/Mic92/sops-nix
#
# for each new user you want to edit sops files:
# create a private age key from ssh key:
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
# if the private key was password protected, then first decrypt it:
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
#
# for each user you want to decrypt secrets:
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
# add the result to .sops.yaml
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
#
# for each host you want to decrypt secrets:
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
# add the result to .sops.yaml
# $ sops updatekeys secrets/example.yaml
#
# to create a new secret:
# $ sops secrets/example.yaml
# control access below (sops.secret.<x>.owner = ...)
#
# to read a secret:
# $ cat /run/secrets/example_key
{ config, ... }:
{ config, lib, sane-lib, ... }:
let
inherit (lib.strings) hasSuffix removeSuffix;
secretsForHost = host: sane-lib.joinAttrsets (
map
(path: lib.optionalAttrs (hasSuffix ".bin" path) (sane-lib.nameValueToAttrs {
name = removeSuffix ".bin" path;
value = {
sopsFile = ../../secrets/${host}/${path};
format = "binary";
};
}))
(sane-lib.enumerateFilePaths ../../secrets/${host})
);
in
{
# SOPS configuration:
# docs: https://github.com/Mic92/sops-nix
#
# for each new user you want to edit sops files:
# create a private age key from ssh key:
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
# if the private key was password protected, then first decrypt it:
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
#
# for each user you want to decrypt secrets:
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
# add the result to .sops.yaml
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
#
# for each host you want to decrypt secrets:
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
# add the result to .sops.yaml
# $ sops updatekeys secrets/example.yaml
#
# to create a new secret:
# $ sops secrets/example.yaml
# control access below (sops.secret.<x>.owner = ...)
#
# to read a secret:
# $ cat /run/secrets/example_key
# sops.age.sshKeyPaths = [ "/home/colin/.ssh/id_ed25519_dec" ];
# This will add secrets.yaml to the nix store
# You can avoid this by adding a string to the full path instead, i.e.
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
sops.defaultSopsFile = ../../secrets/universal.yaml;
sops.gnupg.sshKeyPaths = []; # disable RSA key import
# This is using an age key that is expected to already be in the filesystem
# sops.age.keyFile = "/home/colin/.ssh/age.pub";
@@ -56,15 +45,80 @@ in
# };
# sops.secrets."myservice/my_subdir/my_secret" = {};
sops.secrets = lib.mkMerge [
(secretsForHost "common")
(secretsForHost config.networking.hostName)
{
"jackett_apikey".owner = config.users.users.colin.name;
"mx-sanebot-env".owner = config.users.users.colin.name;
"snippets".owner = config.users.users.colin.name;
}
];
## universal secrets
# TODO: glob these?
sops.secrets."jackett_apikey" = {
sopsFile = ../../secrets/universal.yaml;
owner = config.users.users.colin.name;
};
sops.secrets."router_passwd" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us-atl_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us-mi_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_ukr_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."snippets" = {
sopsFile = ../../secrets/universal/snippets.bin;
format = "binary";
owner = config.users.users.colin.name;
};
sops.secrets."bt/car" = {
sopsFile = ../../secrets/universal/bt/car.bin;
format = "binary";
};
sops.secrets."bt/earbuds" = {
sopsFile = ../../secrets/universal/bt/earbuds.bin;
format = "binary";
};
sops.secrets."bt/portable-speaker" = {
sopsFile = ../../secrets/universal/bt/portable-speaker.bin;
format = "binary";
};
sops.secrets."iwd/community-university.psk" = {
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
format = "binary";
};
sops.secrets."iwd/friend-libertarian-dod.psk" = {
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
format = "binary";
};
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-bedroom.psk" = {
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-shared-24G.psk" = {
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-shared.psk" = {
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
format = "binary";
};
sops.secrets."iwd/iphone" = {
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
format = "binary";
};
sops.secrets."iwd/parents" = {
sopsFile = ../../secrets/universal/net/parents.psk.bin;
format = "binary";
};
}

View File

@@ -30,15 +30,4 @@ in
})
(globalKeys ++ domainKeys)
);
services.openssh = {
enable = true;
settings.PermitRootLogin = "no";
settings.PasswordAuthentication = false;
};
sane.ports.ports."22" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = lib.mkDefault "colin-ssh";
};
}

View File

@@ -33,14 +33,13 @@ in
];
group = "users";
extraGroups = [
"dialout" # required for modem access (moby)
"feedbackd"
"input" # for /dev/input/<xyz>: sxmo
"networkmanager"
"nixbuild"
"video" # phosh/mobile. XXX colin: unsure if necessary
"wheel"
"wireshark"
"nixbuild"
"networkmanager"
# phosh/mobile. XXX colin: unsure if necessary
"video"
"feedbackd"
"dialout" # required for modem access
];
# initial password is empty, in case anything goes wrong.
@@ -98,11 +97,9 @@ in
# convenience
sane.user.fs."knowledge" = fs.wantedSymlinkTo "private/knowledge";
sane.user.fs."nixos" = fs.wantedSymlinkTo "dev/nixos";
sane.user.fs."Books/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Books";
sane.user.fs."Videos/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Videos";
sane.user.fs."Videos/servo-incomplete" = fs.wantedSymlinkTo "/mnt/servo-media/incomplete";
sane.user.fs."Music/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Music";
sane.user.fs."Pictures/servo-macros" = fs.wantedSymlinkTo "/mnt/servo-media/Pictures/macros";
# used by password managers, e.g. unix `pass`
sane.user.fs.".password-store" = fs.wantedSymlinkTo "knowledge/secrets/accounts";
@@ -129,5 +126,11 @@ in
enable = true;
wheelNeedsPassword = false;
};
services.openssh = {
enable = true;
settings.PermitRootLogin = "no";
settings.PasswordAuthentication = false;
};
};
}

View File

@@ -11,7 +11,7 @@ let
def-ovpn = name: { endpoint, publicKey, address }: {
networking.wg-quick.interfaces."ovpnd-${name}" = {
inherit address;
privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path;
privateKeyFile = config.sops.secrets."wg_ovpnd_${name}_privkey".path;
dns = [
"46.227.67.134"
"192.165.9.158"

View File

@@ -4,7 +4,7 @@
{ hostName, localSystem }:
# module args
{ lib, ... }:
{ config, ... }:
{
imports = [
@@ -14,5 +14,14 @@
];
networking.hostName = hostName;
nixpkgs.buildPlatform = lib.mkIf (localSystem != null) localSystem;
nixpkgs.overlays = [
(next: prev: {
# for local != target we by default just emulate the target while building.
# provide a `pkgs.cross.<pkg>` alias that consumers can use instead of `pkgs.<foo>`
# to explicitly opt into non-emulated cross compilation for any specific package.
# this is most beneficial for large packages with few pre-requisites -- like Linux.
cross = next.crossFrom."${localSystem}";
})
];
}

View File

@@ -11,6 +11,5 @@
./roles
./services
./wg-home.nix
./yggdrasil.nix
];
}

View File

@@ -10,7 +10,6 @@ in
./phosh.nix
./plasma.nix
./plasma-mobile.nix
./sway
./sxmo.nix
./sway.nix
];
}

View File

@@ -26,11 +26,9 @@ in
package = null;
suggestedPrograms = [
"guiApps"
"phosh-mobile-settings"
# TODO: see about removing gnome-bluetooth if the in-built gnome-settings bluetooth manager can work
"gnome.gnome-bluetooth"
"gnome.gnome-terminal"
"phosh-mobile-settings"
# "plasma5Packages.konsole" # more reliable terminal
];
};
}
@@ -38,13 +36,9 @@ in
sane.programs = {
inherit (pkgs // {
"gnome.gnome-bluetooth" = pkgs.gnome.gnome-bluetooth;
"gnome.gnome-terminal" = pkgs.gnome.gnome-terminal;
"plasma5Packages.konsole" = pkgs.plasma5Packages.konsole;
})
phosh-mobile-settings
"plasma5Packages.konsole"
# "gnome.gnome-bluetooth"
"gnome.gnome-terminal"
;
};
}
@@ -52,15 +46,7 @@ in
(mkIf cfg.enable {
sane.programs.phoshApps.enableFor.user.colin = true;
# TODO(2023/02/28): remove this qt.style = "gtk2" override.
# gnome by default tells qt to stylize its apps similar to gnome.
# but the package needed for that doesn't cross-compile, hence i disable that here.
# qt.platformTheme = "gtk2";
# qt.style = "gtk2";
# docs: https://github.com/NixOS/nixpkgs/blob/nixos-22.05/nixos/modules/services/x11/desktop-managers/phosh.nix
# docs: <repo:gnome/phosh:src/phoc.ini.example>
# docs: <repo:gnome/phosh:src/settings.c#config_ini_handler>
services.xserver.desktopManager.phosh = {
enable = true;
user = "colin";
@@ -74,26 +60,6 @@ in
};
};
# phosh enables `services.gnome.{core-os-services, core-shell}`
# and this in turn enables some default apps we don't really care about.
# see <nixos/modules/services/x11/desktop-managers/gnome.nix>
environment.gnome.excludePackages = with pkgs; [
# gnome.gnome-menus # unused outside gnome classic, but probably harmless
gnome-tour
];
services.dleyna-renderer.enable = false;
services.dleyna-server.enable = false;
services.gnome.gnome-browser-connector.enable = false;
services.gnome.gnome-initial-setup.enable = false;
services.gnome.gnome-online-accounts.enable = false;
services.gnome.gnome-remote-desktop.enable = false;
services.gnome.gnome-user-share.enable = false;
services.gnome.rygel.enable = false;
# gnome doesn't use mkDefault for these -- unclear why not
services.gnome.evolution-data-server.enable = mkForce false;
services.gnome.gnome-online-miners.enable = mkForce false;
# XXX: phosh enables networkmanager by default; can probably disable these lines
networking.useDHCP = false;
networking.networkmanager.enable = true;
@@ -115,20 +81,7 @@ in
NIXOS_OZONE_WL = "1";
};
systemd.services.phosh.environment = {
# PHOC_DEBUG: comma-separated list of:
# - ``auto-maximize``: Maximize toplevels
# - ``damage-tracking``: Debug damage tracking
# - ``no-quit``: Don't quit when session ends
# - ``touch-points``: Debug touch points
# - ``layer-shell``: Debug layer shell
# - ``cutouts``: Debug display cutouts and notches
PHOC_DEBUG = "layer-shell";
# G_DEBUG, G_MESSAGE_DEBUG for glib debugging: <https://docs.gtk.org/glib/running.html>
};
programs.dconf.packages = [
# org.kde.konsole.desktop
(pkgs.writeTextFile {
name = "dconf-phosh-settings";
destination = "/etc/dconf/db/site.d/00_phosh_settings";
@@ -141,7 +94,7 @@ in
sleep-inactive-battery-timeout=5400
[sm/puri/phosh]
favorites=['gpodder.desktop', 'nheko.desktop', 'sublime-music.desktop', 'firefox.desktop', 'org.gnome.Terminal.desktop']
favorites=['gpodder.desktop', 'nheko.desktop', 'sublime-music.desktop', 'firefox.desktop', 'org.kde.konsole.desktop']
'';
})
];
@@ -156,11 +109,7 @@ in
services.xserver.displayManager.job.preStart = ''
${pkgs.systemd}/bin/busctl call org.freedesktop.Accounts /org/freedesktop/Accounts org.freedesktop.Accounts CacheUser s colin
'';
# XXX for some reason specifying defaultSession = "sm.puri.Phosh" breaks cross-compiled display-manager startup
# - causes an attempt to load x86-64 glib-2.76.2/lib/libglib-2.0.so.0
# - likely <repo:nixpkgs:nixos/modules/services/x11/display-managers/account-service-util.nix>
# - but i believe some variant of this issue existed even during emulated compilation
# services.xserver.displayManager.defaultSession = "sm.puri.Phosh";
# services.xserver.displayManager.defaultSession = "sm.puri.Phosh"; # XXX: not sure why this doesn't propagate correctly.
services.xserver.displayManager.lightdm.extraSeatDefaults = ''
user-session = phosh
'';

View File

@@ -10,6 +10,4 @@ https://jackett.uninsane.org/UI/Dashboard#search=
https://fed.uninsane.org
https://bt.uninsane.org
https://sci-hub.se
https://archive.is
https://news.ycombinator.com
https://192.168.15.1:60481 # Router/Firewall

662
hosts/modules/gui/sway.nix Normal file
View File

@@ -0,0 +1,662 @@
{ config, lib, pkgs, sane-lib, ... }:
# docs: https://nixos.wiki/wiki/Sway
with lib;
let
cfg = config.sane.gui.sway;
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
waybar-config = [
{ # TOP BAR
layer = "top";
height = 40;
modules-left = ["sway/workspaces" "sway/mode"];
modules-center = ["sway/window"];
modules-right = ["custom/mediaplayer" "clock" "battery" "cpu" "network"];
"sway/window" = {
max-length = 50;
};
# include song artist/title. source: https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/
"custom/mediaplayer" = {
exec = pkgs.writeShellScript "waybar-mediaplayer" ''
player_status=$(${pkgs.playerctl}/bin/playerctl status 2> /dev/null)
if [ "$player_status" = "Playing" ]; then
echo "$(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
elif [ "$player_status" = "Paused" ]; then
echo " $(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
fi
'';
interval = 2;
format = "{} ";
# return-type = "json";
on-click = "${pkgs.playerctl}/bin/playerctl play-pause";
on-scroll-up = "${pkgs.playerctl}/bin/playerctl next";
on-scroll-down = "${pkgs.playerctl}/bin/playerctl previous";
};
network = {
# docs: https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd
interval = 2;
max-length = 40;
# custom :> format specifier explained here: https://github.com/Alexays/Waybar/pull/472
format-ethernet = " {bandwidthUpBits:>} {bandwidthDownBits:>}";
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>} {bandwidthDownBits:>}";
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>} {bandwidthDownBits:>}";
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>} {bandwidthDownBits:>}";
format-disconnected = "";
};
cpu = {
format = " {usage:2}%";
tooltip = false;
};
battery = {
states = {
good = 95;
warning = 30;
critical = 10;
};
format = "{icon} {capacity}%";
format-icons = [
""
""
""
""
""
];
};
clock = {
format-alt = "{:%a, %d. %b %H:%M}";
};
}
];
# waybar-config-text = lib.generators.toJSON {} waybar-config;
waybar-config-text = (pkgs.formats.json {}).generate "waybar-config.json" waybar-config;
# bare sway launcher
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
${pkgs.sway}/bin/sway --debug > /tmp/sway.log 2>&1
'';
# start sway and have it construct the gtkgreeter
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /tmp/sway-as-greeter.log 2>&1
'';
# (config file for the above)
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
exec "${gtkgreet-launcher}"
'';
# gtkgreet which launches a layered sway instance
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
# NB: the "command" field here is run in the user's shell.
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
'';
greeter-session = {
# greeter session config
command = "${sway-as-greeter}/bin/sway-as-greeter";
# alternatives:
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
};
greeterless-session = {
# no greeter
command = "${sway-launcher}/bin/sway-launcher";
user = "colin";
};
in
{
options = {
sane.gui.sway.enable = mkOption {
default = false;
type = types.bool;
};
sane.gui.sway.useGreeter = mkOption {
description = ''
launch sway via a greeter (like greetd's gtkgreet).
sway is usable without a greeter, but skipping the greeter means no PAM session.
'';
default = true;
type = types.bool;
};
};
config = mkMerge [
{
sane.programs.swayApps = {
package = null;
suggestedPrograms = [
"guiApps"
"swaylock"
"swayidle"
"wl-clipboard"
"mako" # notification daemon
# # "pavucontrol"
"gnome.gnome-bluetooth"
"gnome.gnome-control-center"
];
};
}
{
sane.programs = {
inherit (pkgs // {
"gnome.gnome-bluetooth" = pkgs.gnome.gnome-bluetooth;
"gnome.gnome-control-center" = pkgs.gnome.gnome-control-center;
})
swaylock
swayidle
wl-clipboard
mako
"gnome.gnome-bluetooth"
"gnome.gnome-control-center"
;
};
}
(mkIf cfg.enable {
sane.programs.swayApps.enableFor.user.colin = true;
# swap in these lines to use SDDM instead of `services.greetd`.
# services.xserver.displayManager.sddm.enable = true;
# services.xserver.enable = true;
services.greetd = {
# greetd source/docs:
# - <https://git.sr.ht/~kennylevinsen/greetd>
enable = true;
settings = {
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
};
};
# we need the greeter's command to be on our PATH
users.users.colin.packages = [ sway-launcher ];
# some programs (e.g. fractal) **require** a "Secret Service Provider"
services.gnome.gnome-keyring.enable = true;
# unlike other DEs, sway configures no audio stack
# administer with pw-cli, pw-mon, pw-top commands
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true; # ??
pulse.enable = true;
};
networking.useDHCP = false;
networking.networkmanager.enable = true;
networking.wireless.enable = lib.mkForce false;
hardware.bluetooth.enable = true;
services.blueman.enable = true;
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
services.gnome.gnome-settings-daemon.enable = true;
# start the components of gsd we need at login
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
# it doesn't actually seem to need ANY of them in the first place T_T
systemd.user.targets."gnome-session-initialized".enable = false;
# bluez can't connect to audio devices unless pipewire is running.
# a system service can't depend on a user service, so just launch it at graphical-session
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
programs.sway = {
enable = true;
wrapperFeatures.gtk = true;
};
sane.user.fs.".config/sway/config" =
let
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
sed = "${pkgs.gnused}/bin/sed";
wtype = "${pkgs.wtype}/bin/wtype";
kitty = "${pkgs.kitty}/bin/kitty";
launcher-cmd = fuzzel;
terminal-cmd = kitty;
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
snip-file = ./snippets.txt;
# TODO: querying sops here breaks encapsulation
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
strip-comments = "${sed} 's/ #.*$//'";
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
# TODO: next splatmoji release should allow `-s none` to disable skin tones
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
in sane-lib.fs.wantedText ''
### default font
font pango:monospace 8
### pixel boundary between windows
default_border pixel 3
default_floating_border pixel 2
hide_edge_borders smart
### defaults
focus_wrapping no
focus_follows_mouse yes
focus_on_window_activation smart
mouse_warping output
workspace_layout default
workspace_auto_back_and_forth no
### default colors (#border #background #text #indicator #childBorder)
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
client.unfocused #333333 #222222 #888888 #292d2e #222222
client.urgent #2f343a #900000 #ffffff #900000 #900000
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
client.background #ffffff
### key bindings
floating_modifier Mod1
## media keys
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
bindsym Mod1+Page_Up exec ${vol-up-cmd}
bindsym Mod1+Page_Down exec ${vol-down-cmd}
bindsym XF86AudioMute exec ${mute-cmd}
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
## special functions
bindsym Mod1+Print exec ${screenshot-cmd}
bindsym Mod1+l exec ${lock-cmd}
bindsym Mod1+s exec ${snip-cmd}
bindsym Mod1+slash exec ${emoji-cmd}
bindsym Mod1+d exec ${launcher-cmd}
bindsym Mod1+Return exec ${terminal-cmd}
bindsym Mod1+Shift+q kill
bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
bindsym Mod1+Shift+c reload
## layout
bindsym Mod1+b splith
bindsym Mod1+v splitv
bindsym Mod1+f fullscreen toggle
bindsym Mod1+a focus parent
bindsym Mod1+w layout tabbed
bindsym Mod1+e layout toggle split
bindsym Mod1+Shift+space floating toggle
bindsym Mod1+space focus mode_toggle
bindsym Mod1+r mode resize
## movement
bindsym Mod1+Up focus up
bindsym Mod1+Down focus down
bindsym Mod1+Left focus left
bindsym Mod1+Right focus right
bindsym Mod1+Shift+Up move up
bindsym Mod1+Shift+Down move down
bindsym Mod1+Shift+Left move left
bindsym Mod1+Shift+Right move right
## workspaces
bindsym Mod1+1 workspace number 1
bindsym Mod1+2 workspace number 2
bindsym Mod1+3 workspace number 3
bindsym Mod1+4 workspace number 4
bindsym Mod1+5 workspace number 5
bindsym Mod1+6 workspace number 6
bindsym Mod1+7 workspace number 7
bindsym Mod1+8 workspace number 8
bindsym Mod1+9 workspace number 9
bindsym Mod1+Shift+1 move container to workspace number 1
bindsym Mod1+Shift+2 move container to workspace number 2
bindsym Mod1+Shift+3 move container to workspace number 3
bindsym Mod1+Shift+4 move container to workspace number 4
bindsym Mod1+Shift+5 move container to workspace number 5
bindsym Mod1+Shift+6 move container to workspace number 6
bindsym Mod1+Shift+7 move container to workspace number 7
bindsym Mod1+Shift+8 move container to workspace number 8
bindsym Mod1+Shift+9 move container to workspace number 9
## "scratchpad" = ??
bindsym Mod1+Shift+minus move scratchpad
bindsym Mod1+minus scratchpad show
### defaults
mode "resize" {
bindsym Down resize grow height 10 px
bindsym Escape mode default
bindsym Left resize shrink width 10 px
bindsym Return mode default
bindsym Right resize grow width 10 px
bindsym Up resize shrink height 10 px
bindsym h resize shrink width 10 px
bindsym j resize grow height 10 px
bindsym k resize shrink height 10 px
bindsym l resize grow width 10 px
}
### lightly modified bars
bar {
# TODO: fonts was:
# config.fonts.fontconfig.defaultFonts; (monospace ++ emoji)
font pango:Hack, Font Awesome 6 Free, Twitter Color Emoji 24.000000
mode dock
hidden_state hide
position top
status_command ${pkgs.i3status}/bin/i3status
swaybar_command ${pkgs.waybar}/bin/waybar
workspace_buttons yes
strip_workspace_numbers no
tray_output primary
colors {
background #000000
statusline #ffffff
separator #666666
# #border #background #text
focused_workspace #4c7899 #285577 #ffffff
active_workspace #333333 #5f676a #ffffff
inactive_workspace #333333 #222222 #888888
urgent_workspace #2f343a #900000 #ffffff
binding_mode #2f343a #900000 #ffffff
}
}
### displays
## DESKTOP
output "Samsung Electric Company S22C300 0x00007F35" {
pos 0,0
res 1920x1080
}
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
pos 1920,0
res 3440x1440
}
## LAPTOP
# sh/en TV
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
pos 0,0
res 1920x1080
}
# internal display
output "Unknown 0x0637 0x00000000" {
pos 1920,0
res 1920x1080
}
'';
sane.user.fs.".config/waybar/config" = sane-lib.fs.wantedSymlinkTo waybar-config-text;
# style docs: https://github.com/Alexays/Waybar/wiki/Styling
sane.user.fs.".config/waybar/style.css" = sane-lib.fs.wantedText ''
* {
font-family: monospace;
}
/* defaults below: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
window#waybar {
background-color: rgba(43, 48, 59, 0.5);
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
color: #ffffff;
transition-property: background-color;
transition-duration: .5s;
}
window#waybar.hidden {
opacity: 0.2;
}
/*
window#waybar.empty {
background-color: transparent;
}
window#waybar.solo {
background-color: #FFFFFF;
}
*/
window#waybar.termite {
background-color: #3F3F3F;
}
window#waybar.chromium {
background-color: #000000;
border: none;
}
#workspaces button {
padding: 0 5px;
background-color: transparent;
color: #ffffff;
/* Use box-shadow instead of border so the text isn't offset */
box-shadow: inset 0 -3px transparent;
/* Avoid rounded borders under each workspace name */
border: none;
border-radius: 0;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button:hover {
background: rgba(0, 0, 0, 0.2);
box-shadow: inset 0 -3px #ffffff;
}
#workspaces button.focused {
background-color: #64727D;
box-shadow: inset 0 -3px #ffffff;
}
#workspaces button.urgent {
background-color: #eb4d4b;
}
#mode {
background-color: #64727D;
border-bottom: 3px solid #ffffff;
}
#clock,
#battery,
#cpu,
#memory,
#disk,
#temperature,
#backlight,
#network,
#pulseaudio,
#custom-media,
#tray,
#mode,
#idle_inhibitor,
#mpd {
padding: 0 10px;
color: #ffffff;
}
#window,
#workspaces {
margin: 0 4px;
}
/* If workspaces is the leftmost module, omit left margin */
.modules-left > widget:first-child > #workspaces {
margin-left: 0;
}
/* If workspaces is the rightmost module, omit right margin */
.modules-right > widget:last-child > #workspaces {
margin-right: 0;
}
#clock {
background-color: #64727D;
}
#battery {
background-color: #ffffff;
color: #000000;
}
#battery.charging, #battery.plugged {
color: #ffffff;
background-color: #26A65B;
}
@keyframes blink {
to {
background-color: #ffffff;
color: #000000;
}
}
#battery.critical:not(.charging) {
background-color: #f53c3c;
color: #ffffff;
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
label:focus {
background-color: #000000;
}
#cpu {
background-color: #2ecc71;
color: #000000;
}
#memory {
background-color: #9b59b6;
}
#disk {
background-color: #964B00;
}
#backlight {
background-color: #90b1b1;
}
#network {
background-color: #2980b9;
}
#network.disconnected {
background-color: #f53c3c;
}
#pulseaudio {
background-color: #f1c40f;
color: #000000;
}
#pulseaudio.muted {
background-color: #90b1b1;
color: #2a5c45;
}
#custom-media {
background-color: #66cc99;
color: #2a5c45;
min-width: 100px;
}
#custom-media.custom-spotify {
background-color: #66cc99;
}
#custom-media.custom-vlc {
background-color: #ffa000;
}
#temperature {
background-color: #f0932b;
}
#temperature.critical {
background-color: #eb4d4b;
}
#tray {
background-color: #2980b9;
}
#tray > .passive {
-gtk-icon-effect: dim;
}
#tray > .needs-attention {
-gtk-icon-effect: highlight;
background-color: #eb4d4b;
}
#idle_inhibitor {
background-color: #2d3436;
}
#idle_inhibitor.activated {
background-color: #ecf0f1;
color: #2d3436;
}
#mpd {
background-color: #66cc99;
color: #2a5c45;
}
#mpd.disconnected {
background-color: #f53c3c;
}
#mpd.stopped {
background-color: #90b1b1;
}
#mpd.paused {
background-color: #51a37a;
}
#language {
background: #00b093;
color: #740864;
padding: 0 5px;
margin: 0 5px;
min-width: 16px;
}
#keyboard-state {
background: #97e1ad;
color: #000000;
padding: 0 0px;
margin: 0 5px;
min-width: 16px;
}
#keyboard-state > label {
padding: 0 5px;
}
#keyboard-state > label.locked {
background: rgba(0, 0, 0, 0.2);
}
'';
# style = ''
# * {
# border: none;
# border-radius: 0;
# font-family: Source Code Pro;
# }
# window#waybar {
# background: #16191C;
# color: #AAB2BF;
# }
# #workspaces button {
# padding: 0 5px;
# }
# .custom-spotify {
# padding: 0 10px;
# margin: 0 4px;
# background-color: #1DB954;
# color: black;
# }
# '';
})
];
}

View File

@@ -1,164 +0,0 @@
{ config, lib, pkgs, sane-lib, ... }:
# docs: https://nixos.wiki/wiki/Sway
with lib;
let
cfg = config.sane.gui.sway;
# bare sway launcher
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
${pkgs.sway}/bin/sway --debug > /var/log/sway/sway.log 2>&1
'';
# start sway and have it construct the gtkgreeter
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /var/log/sway/sway-as-greeter.log 2>&1
'';
# (config file for the above)
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
exec "${gtkgreet-launcher}"
'';
# gtkgreet which launches a layered sway instance
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
# NB: the "command" field here is run in the user's shell.
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
'';
greeter-session = {
# greeter session config
command = "${sway-as-greeter}/bin/sway-as-greeter";
# alternatives:
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
};
greeterless-session = {
# no greeter
command = "${sway-launcher}/bin/sway-launcher";
user = "colin";
};
in
{
options = {
sane.gui.sway.enable = mkOption {
default = false;
type = types.bool;
};
sane.gui.sway.useGreeter = mkOption {
description = ''
launch sway via a greeter (like greetd's gtkgreet).
sway is usable without a greeter, but skipping the greeter means no PAM session.
'';
default = true;
type = types.bool;
};
};
config = mkMerge [
{
sane.programs.swayApps = {
package = null;
suggestedPrograms = [
"guiApps"
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
"swaylock"
"swayidle"
"wl-clipboard"
"blueberry" # GUI bluetooth manager
"mako" # notification daemon
# # "pavucontrol"
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
"gnome.gnome-control-center"
"sway-contrib.grimshot"
];
};
}
{
sane.programs = {
inherit (pkgs // {
"gnome.gnome-bluetooth" = pkgs.gnome.gnome-bluetooth;
"gnome.gnome-control-center" = pkgs.gnome.gnome-control-center;
"sway-contrib.grimshot" = pkgs.sway-contrib.grimshot;
})
swaylock
swayidle
wl-clipboard
blueberry
mako
"gnome.gnome-bluetooth"
"gnome.gnome-control-center"
"sway-contrib.grimshot"
;
};
}
(mkIf cfg.enable {
sane.programs.swayApps.enableFor.user.colin = true;
# swap in these lines to use SDDM instead of `services.greetd`.
# services.xserver.displayManager.sddm.enable = true;
# services.xserver.enable = true;
services.greetd = {
# greetd source/docs:
# - <https://git.sr.ht/~kennylevinsen/greetd>
enable = true;
settings = {
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
};
};
# we need the greeter's command to be on our PATH
users.users.colin.packages = [ sway-launcher ];
# some programs (e.g. fractal) **require** a "Secret Service Provider"
services.gnome.gnome-keyring.enable = true;
# unlike other DEs, sway configures no audio stack
# administer with pw-cli, pw-mon, pw-top commands
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true; # ??
pulse.enable = true;
};
networking.useDHCP = false;
networking.networkmanager.enable = true;
networking.wireless.enable = lib.mkForce false;
hardware.bluetooth.enable = true;
services.blueman.enable = true;
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
services.gnome.gnome-settings-daemon.enable = true;
# start the components of gsd we need at login
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
# it doesn't actually seem to need ANY of them in the first place T_T
systemd.user.targets."gnome-session-initialized".enable = false;
# bluez can't connect to audio devices unless pipewire is running.
# a system service can't depend on a user service, so just launch it at graphical-session
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
sane.fs."/var/log/sway" = {
dir.acl.mode = "0777";
wantedBeforeBy = [ "greetd.service" "display-manager.service" ];
};
programs.sway = {
enable = true;
wrapperFeatures.gtk = true;
};
sane.user.fs.".config/sway/config" = sane-lib.fs.wantedText
(import ./sway-config.nix { inherit config pkgs; });
sane.user.fs.".config/waybar/config" =
let
waybar-config = import ./waybar-config.nix { inherit pkgs; };
in sane-lib.fs.wantedSymlinkTo (
(pkgs.formats.json {}).generate "waybar-config.json" waybar-config
);
sane.user.fs.".config/waybar/style.css" = sane-lib.fs.wantedText
(builtins.readFile ./waybar-style.css);
})
];
}

View File

@@ -1,174 +0,0 @@
{ pkgs, config }:
let
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
sed = "${pkgs.gnused}/bin/sed";
wtype = "${pkgs.wtype}/bin/wtype";
kitty = "${pkgs.kitty}/bin/kitty";
launcher-cmd = fuzzel;
terminal-cmd = kitty;
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
snip-file = ../snippets.txt;
# TODO: querying sops here breaks encapsulation
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
strip-comments = "${sed} 's/ #.*$//'";
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
# TODO: next splatmoji release should allow `-s none` to disable skin tones
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
in ''
### default font
font pango:monospace 8
### pixel boundary between windows
default_border pixel 3
default_floating_border pixel 2
hide_edge_borders smart
### defaults
focus_wrapping no
focus_follows_mouse yes
focus_on_window_activation smart
mouse_warping output
workspace_layout default
workspace_auto_back_and_forth no
### default colors (#border #background #text #indicator #childBorder)
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
client.unfocused #333333 #222222 #888888 #292d2e #222222
client.urgent #2f343a #900000 #ffffff #900000 #900000
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
client.background #ffffff
### key bindings
floating_modifier Mod1
## media keys
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
bindsym Mod1+Page_Up exec ${vol-up-cmd}
bindsym Mod1+Page_Down exec ${vol-down-cmd}
bindsym XF86AudioMute exec ${mute-cmd}
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
## special functions
bindsym Mod1+Print exec ${screenshot-cmd}
bindsym Mod1+l exec ${lock-cmd}
bindsym Mod1+s exec ${snip-cmd}
bindsym Mod1+slash exec ${emoji-cmd}
bindsym Mod1+d exec ${launcher-cmd}
bindsym Mod1+Return exec ${terminal-cmd}
bindsym Mod1+Shift+q kill
bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
bindsym Mod1+Shift+c reload
## layout
bindsym Mod1+b splith
bindsym Mod1+v splitv
bindsym Mod1+f fullscreen toggle
bindsym Mod1+a focus parent
bindsym Mod1+w layout tabbed
bindsym Mod1+e layout toggle split
bindsym Mod1+Shift+space floating toggle
bindsym Mod1+space focus mode_toggle
bindsym Mod1+r mode resize
## movement
bindsym Mod1+Up focus up
bindsym Mod1+Down focus down
bindsym Mod1+Left focus left
bindsym Mod1+Right focus right
bindsym Mod1+Shift+Up move up
bindsym Mod1+Shift+Down move down
bindsym Mod1+Shift+Left move left
bindsym Mod1+Shift+Right move right
## workspaces
bindsym Mod1+1 workspace number 1
bindsym Mod1+2 workspace number 2
bindsym Mod1+3 workspace number 3
bindsym Mod1+4 workspace number 4
bindsym Mod1+5 workspace number 5
bindsym Mod1+6 workspace number 6
bindsym Mod1+7 workspace number 7
bindsym Mod1+8 workspace number 8
bindsym Mod1+9 workspace number 9
bindsym Mod1+Shift+1 move container to workspace number 1
bindsym Mod1+Shift+2 move container to workspace number 2
bindsym Mod1+Shift+3 move container to workspace number 3
bindsym Mod1+Shift+4 move container to workspace number 4
bindsym Mod1+Shift+5 move container to workspace number 5
bindsym Mod1+Shift+6 move container to workspace number 6
bindsym Mod1+Shift+7 move container to workspace number 7
bindsym Mod1+Shift+8 move container to workspace number 8
bindsym Mod1+Shift+9 move container to workspace number 9
## "scratchpad" = ??
bindsym Mod1+Shift+minus move scratchpad
bindsym Mod1+minus scratchpad show
### defaults
mode "resize" {
bindsym Down resize grow height 10 px
bindsym Escape mode default
bindsym Left resize shrink width 10 px
bindsym Return mode default
bindsym Right resize grow width 10 px
bindsym Up resize shrink height 10 px
bindsym h resize shrink width 10 px
bindsym j resize grow height 10 px
bindsym k resize shrink height 10 px
bindsym l resize grow width 10 px
}
### lightly modified bars
bar {
# TODO: fonts was:
# config.fonts.fontconfig.defaultFonts; (monospace ++ emoji)
font pango:Hack, Font Awesome 6 Free, Twitter Color Emoji 24.000000
mode dock
hidden_state hide
position top
status_command ${pkgs.i3status}/bin/i3status
swaybar_command ${pkgs.waybar}/bin/waybar
workspace_buttons yes
strip_workspace_numbers no
tray_output primary
colors {
background #000000
statusline #ffffff
separator #666666
# #border #background #text
focused_workspace #4c7899 #285577 #ffffff
active_workspace #333333 #5f676a #ffffff
inactive_workspace #333333 #222222 #888888
urgent_workspace #2f343a #900000 #ffffff
binding_mode #2f343a #900000 #ffffff
}
}
### displays
## DESKTOP
output "Samsung Electric Company S22C300 0x00007F35" {
pos 0,0
res 1920x1080
}
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
pos 1920,0
res 3440x1440
}
## LAPTOP
# sh/en TV
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
pos 0,0
res 1920x1080
}
# internal display
output "Unknown 0x0637 0x00000000" {
pos 1920,0
res 1920x1080
}
''

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