Compare commits

..

27 Commits

Author SHA1 Message Date
790adeee5f cross compilation: build webp-pixbuf-loader without emulation 2023-02-21 00:05:37 +00:00
72c6f34043 cross: compile appstream, colord without emulation 2023-02-20 20:24:37 +00:00
6108c12631 pins: disable failing python310Packages.ipython tests 2023-02-20 20:23:32 +00:00
ba93870b51 pins: disable failing python310Packages.pytest-xdist tests 2023-02-19 21:42:27 +00:00
a871d4eb3d WIP: reduce emulated cross pkgset; aborted x86-x86 cross compilation 2023-02-18 11:58:04 +00:00
567b20ddde moby: ship the full recommended guiApps again 2023-02-18 11:57:16 +00:00
556cb1a2d0 phosh: disable konsole. it drags in a large closure, especially qt stuff which doesn't cross compile well 2023-02-18 11:57:16 +00:00
f40427245f cross packages: full moby build can cross compile (except konsole is untested) 2023-02-18 11:57:16 +00:00
ce1ba59413 programs: split the guiApps category into slightly smaller bits 2023-02-18 11:57:16 +00:00
691a6ef045 moby: ship some of the base apps 2023-02-18 11:57:16 +00:00
2fc99d8b5a cross packages: cross compile more stuff 2023-02-18 11:57:16 +00:00
80de7248ed gpodder: fix makeWrapper to be a nativeBuildInput 2023-02-18 11:57:16 +00:00
2fd16348e1 phosh-mobile-settings: cross compile 2023-02-18 11:57:16 +00:00
fcc1cb20d5 cross packages: cross compile more gnome packages 2023-02-18 11:57:16 +00:00
10be547077 working moby cross compilation 2023-02-18 11:57:16 +00:00
db1c5d16b5 cross packages: get more things to build and further reduce the emulated closure 2023-02-18 11:57:16 +00:00
a52f3d723a cross packages: get more things to build and further reduce the emulated closure 2023-02-18 11:57:16 +00:00
5a09f7a89d cross packages: reduce the emulated package set 2023-02-18 11:57:16 +00:00
f979dff5aa phosh: disable gvfs service 2023-02-18 11:57:16 +00:00
fd015f61b7 phosh: enable fewer gnome services/packages 2023-02-18 11:57:16 +00:00
7e5d86f931 programs: re-enable cdrtools, fwupd; disable duplicity/backblaze 2023-02-18 11:57:16 +00:00
b920b4713c tune emulated package set (e.g. only emulate one of gvfs' deps; not the WHOLE thing 2023-02-18 11:57:16 +00:00
7e58e39a5f WIP: moby: partially working cross compilation for phosh w/o apps (webkitgtk build OOMs) 2023-02-18 11:57:16 +00:00
98de5043cf moby: get working cross compilation for the base image (i.e. no GUI) 2023-02-18 11:57:16 +00:00
cda319b07a WIP: moby-cross: is cross-by-default instead of opt-in 2023-02-18 11:57:16 +00:00
0a0640cc43 gjs, libadwaita, libsecret: skip flakey check phase 2023-02-18 11:57:16 +00:00
035c4d65f8 mesa: downgrade from 22.3.4 -> 22.3.2 to address a memory leak 2023-02-18 11:57:14 +00:00
649 changed files with 9273 additions and 125176 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,2 @@
/keep
result
result-*
/secrets/local.nix
/working

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

123
README.md
View File

@@ -1,123 +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 (Pinephone)
everything outside of <./hosts/> and <./secrets/> is intended for export, to be importable for use by 3rd parties.
the only hard dependency for my exported pkgs/modules should be [nixpkgs][nixpkgs].
building <./hosts/> will require [sops][sops].
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
- [`sxmo-utils-latest`](./pkgs/additional/sxmo-utils/default.nix)
- [example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)
- [my implementation of impermanence](./modules/persist/default.nix)
- my way of deploying dotfiles/configuring programs per-user:
- <./modules/fs/default.nix>
- <./modules/programs.nix>
- <./modules/users.nix>
[nixpkgs]: https://github.com/NixOS/nixpkgs
[sops]: https://github.com/Mic92/sops-nix
[uninsane-org]: https://uninsane.org
## 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)
or follow the instructions [here][NUR] to use it via the Nix User Repositories.
[NUR]: https://nur.nix-community.org/
## Layout
- `doc/`
- instructions for tasks i find myself doing semi-occasionally in this repo.
- `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 aren't reachable on a deployed system, but may aid manual deployments
- `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
## 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).
patches, for this repo or any other i host, will be warmly welcomed in any manner you see fit:
`git send-email`, DM'ing the patch over Matrix/Lemmy/ActivityPub/etc, even a literal PR where you
link me to your own clone.

113
TODO.md
View File

@@ -1,113 +0,0 @@
## BUGS
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
- else DNS fails
- ringer (i.e. dino incoming call) doesn't prevent moby from sleeping
## 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 sxmo module usable by NUR consumers
- 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
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
- remove `libsForQt5.callPackage` broadly: <https://github.com/NixOS/nixpkgs/issues/180841>
#### upstreaming to non-nixpkgs repos
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
- sxmo: add new app entries
## IMPROVEMENTS:
### security/resilience
- matrix/ntfy: automatically add the ntfy.uninsane.org push URL as part of synapse launch
- ntfy: use a more secure topic
- 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.
- **systemd-run** takes a command and runs it in a temporary scope (cgroup)
- presumably uses the same options as systemd services
- see e.g. <https://github.com/NixOS/nixpkgs/issues/113903#issuecomment-857296349>
- flatpak does this, somehow
- apparmor? SElinux? (desktop) "portals"?
- see Spectrum OS; Alyssa Ross; etc
- bubblewrap-based sandboxing: <https://github.com/nixpak/nixpak>
- canaries for important services
- e.g. daily email checks; daily backup checks
- integrate `nix check` into Gitea actions?
### user experience
#### moby
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
- install apps:
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
- shopping list: <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
- offline Wikipedia
- SwayNC:
- don't show MPRIS if no players detected
- this is a problem of playerctld, i guess
- add option to change audio output
- fix colors (red alert) to match overall theme
- extend width to 100% of portrait mode
- moby: tune GPS
- run only geoclue, and not gpsd, to save power?
- tune QGPS setting in eg25-control, for less jitter?
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
- configure geoclue to do some smoothing?
- manually do smoothing, as some layer between mepo and geoclue/gpsd?
- moby: show battery state on ssh login
- moby: improve gPodder launch time
- sxmo: port to swaybar like i use on desktop
- users in #sxmo claim it's way better perf
- sxmo: fix youtube scripts (package youtube-cli)
- moby: theme GTK apps (i.e. non-adwaita styles)
- combine multiple icon themes to get one which has the full icon set?
- get adwaita-icon-theme to ship everything even when cross-compiled?
- especially, make the menubar collapsible
- try Gradience tool specifically for theming adwaita? <https://linuxphoneapps.org/apps/com.github.gradienceteam.gradience/>
- phog: remove the gnome-shell runtime dependency to save hella closure size
#### non-moby
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
- Helix: make copy-to-system clipboard be the default
- firefox/librewolf: persist history
- just not cookies or tabs
- 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)
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
- sane-bt-search: show details like 5.1 vs stereo, h264 vs h265
- maybe just color these "keywords" in all search results?
- uninsane.org: make URLs relative to allow local use (and as offline homepage)
- email: fix so that local mail doesn't go to junk
- git sendmail flow adds the DKIM signatures, but gets delivered locally w/o having the sig checked, so goes into Junk
- could change junk filter from "no DKIM success" to explicit "DKIM failed"
### perf
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
- would be super handy for package prototyping!
- get moby to build without binfmt emulation (i.e. make all emulation explicit)
- then i can distribute builds across servo + desko, and also allow servo to pull packages from desko w/o worrying about purity
## NEW FEATURES:
- migrate MAME cabinet to nix
- boot it from PXE from servo?
- deploy to new server, and use it as a remote builder
- enable IPv6
- package lemonade lemmy app: <https://linuxphoneapps.org/apps/ml.mdwalters.lemonade/>

View File

@@ -1,9 +0,0 @@
# limited, non-flake interface to this repo.
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
#
# the primary purpose of this file is so i can run `updateScript`s which expect
# the root to be `default.nix`
{ pkgs ? import <nixpkgs> {} }:
pkgs.appendOverlays [
(import ./overlays/all.nix)
]

View File

@@ -1,13 +0,0 @@
to ship `pkgs.foo` on some host, either:
- add it as an entry in `suggestedPrograms` to the appropriate category in `hosts/common/programs/assorted.nix`, or
- `sane.programs.foo.enableFor.user.colin = true` in `hosts/by-name/myhost/default.nix`
if the program needs customization (persistence, configs, secrets):
- add a file for it at `hosts/common/programs/<foo>.nix`
- set the options, `sane.programs.foo.{fs,persist}`
if it's unclear what fs paths a program uses:
- run one of these commands, launch the program, run it again, and `diff`:
- `du -x --apparent-size ~`
- `find ~ -xdev`
- or, inspect the whole tmpfs root with `ncdu -x /`

81
flake.lock generated
View File

@@ -1,15 +1,12 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1687709756,
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@@ -21,43 +18,59 @@
"mobile-nixos": {
"flake": false,
"locked": {
"lastModified": 1694749521,
"narHash": "sha256-MiVokKlpcJmfoGuWAMeW1En7gZ5hk0rCQArYm6P9XCc=",
"lastModified": 1676240485,
"narHash": "sha256-bef1Zrfpo9cxaf19QhqfTwaagpeoNc08sc8OjYDjSnQ=",
"owner": "nixos",
"repo": "mobile-nixos",
"rev": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
"rev": "8701fcb1448f1eb67c0d47631ec2bdb613bd6a38",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "d25d3b87e7f300d8066e31d792337d9cd7ecd23b",
"repo": "mobile-nixos",
"type": "github"
}
},
"nixpkgs": {
"inputs": {
"nixpkgs": [
"nixpkgs-unpatched"
]
},
"locked": {
"lastModified": 1,
"narHash": "sha256-AJlQHunLsnhZ8LdYirwIcqD1iojYJEQAdxGfJn9siPs=",
"path": "/nix/store/7s2pgwqd5ch6n53mh2v8hw7d1zp1r654-source/nixpatches",
"type": "path"
},
"original": {
"path": "/nix/store/7s2pgwqd5ch6n53mh2v8hw7d1zp1r654-source/nixpatches",
"type": "path"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1698544399,
"narHash": "sha256-vhRmPyEyoPkrXF2iykBsWHA05MIaOSmMRLMF7Hul6+s=",
"lastModified": 1676162277,
"narHash": "sha256-GK3cnvKNo1l0skGYXXiLJ/TLqdKyIYXd7jOlo0gN+Qw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d87c5d8c41c9b3b39592563242f3a448b5cc4bc9",
"rev": "d863ca850a06d91365c01620dcac342574ecf46f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"ref": "release-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unpatched": {
"locked": {
"lastModified": 1698611440,
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
"lastModified": 1676569297,
"narHash": "sha256-2n4C4H3/U+3YbDrQB6xIw7AaLdFISCCFwOkcETAigqU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
"rev": "ac1f5b72a9e95873d1de0233fddcb56f99884b37",
"type": "github"
},
"original": {
@@ -70,6 +83,7 @@
"root": {
"inputs": {
"mobile-nixos": "mobile-nixos",
"nixpkgs": "nixpkgs",
"nixpkgs-unpatched": "nixpkgs-unpatched",
"sops-nix": "sops-nix",
"uninsane-dot-org": "uninsane-dot-org"
@@ -78,16 +92,16 @@
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs-unpatched"
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1698548647,
"narHash": "sha256-7c03OjBGqnwDW0FBaBc+NjfEBxMkza+dxZGJPyIzfFE=",
"lastModified": 1676171095,
"narHash": "sha256-2laeSjBAAJ9e/C3uTIPb287iX8qeVLtWiilw1uxqG+A=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "632c3161a6cc24142c8e3f5529f5d81042571165",
"rev": "c5dab21d8706afc7ceb05c23d4244dcb48d6aade",
"type": "github"
},
"original": {
@@ -96,34 +110,19 @@
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"uninsane-dot-org": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unpatched"
"nixpkgs"
]
},
"locked": {
"lastModified": 1698634059,
"narHash": "sha256-+Oyv6vDyCtBzab/5cTG0nUrHD9gj7KgGfD4D1Rn4fCk=",
"lastModified": 1675131883,
"narHash": "sha256-yBgJDG72YqIr1bltasqHD1E/kHc9uRFgDjxDmy6kI8M=",
"ref": "refs/heads/master",
"rev": "2419750ca98fc04af42c91e50c49a29c68d465d2",
"revCount": 210,
"rev": "b099c24091cc192abf3997b94342d4b31cc5757b",
"revCount": 170,
"type": "git",
"url": "https://git.uninsane.org/colin/uninsane"
},

456
flake.nix
View File

@@ -4,8 +4,6 @@
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
# - this would allow for the same optimizations as today's `github:nixos/nixpkgs`, but without obscuring the source.
# a code reader could view the source being referenced simply by clicking the https:// portion of that URI.
# - need some way to apply local patches to inputs.
#
#
@@ -14,64 +12,40 @@
# - 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
# preferably, i would rewrite the human-readable https URLs to nix-specific github: URLs with a helper,
# but `inputs` is required to be a strict attrset: not an expression.
inputs = {
# 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-22.11>
# nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
# <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>
# only used for building disk images, not relevant after deployment
# TODO: replace with something else. commit `0f3ac0bef1aea70254a3bae35e3cc2561623f4c1`
# replaces the imageBuilder with a "new implementation from celun" and wildly breaks my use.
# pinning to d25d3b... is equivalent to holding at 2023-09-15
url = "github:nixos/mobile-nixos?ref=d25d3b87e7f300d8066e31d792337d9cd7ecd23b";
url = "github:nixos/mobile-nixos";
flake = false;
};
sops-nix = {
# <https://github.com/Mic92/sops-nix>
# used to distribute secrets to my hosts
url = "github:Mic92/sops-nix";
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
inputs.nixpkgs.follows = "nixpkgs";
};
uninsane-dot-org = {
# provides the package to deploy <https://uninsane.org>, used only when building the servo host
url = "git+https://git.uninsane.org/colin/uninsane";
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
nixpkgs-unpatched,
mobile-nixos,
sops-nix,
@@ -79,96 +53,52 @@
...
}@inputs:
let
inherit (builtins) attrNames elem listToAttrs map mapAttrs;
# redefine some nixpkgs `lib` functions to avoid the infinite recursion
# of if we tried to use patched `nixpkgs.lib` as part of the patching process.
mapAttrs' = f: set:
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
optionalAttrs = cond: attrs: if cond then attrs else {};
# mapAttrs but without the `name` argument
mapAttrValues = f: mapAttrs (_: f);
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
# 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;
} // {
# provide values that nixpkgs ordinarily sources from the flake.lock file,
# inaccessible to it here because of the import-from-derivation.
# rev and shortRev seem to not always exist (e.g. if the working tree is dirty),
# so those are made conditional.
#
# these values impact the name of a produced nixos system. having date/rev in the
# `readlink /run/current-system` store path helps debuggability.
inherit (self) lastModifiedDate lastModified;
} // optionalAttrs (self ? rev) {
inherit (self) rev;
} // optionalAttrs (self ? shortRev) {
inherit (self) shortRev;
};
nixpkgsCompiledBy = system: nixpkgs.legacyPackages."${system}";
evalHost = { name, local, target }: nixpkgs.lib.nixosSystem {
system = target;
modules = [
{
nixpkgs = (if (local != null) then {
buildPlatform = local;
} else {}) // {
# TODO: does the earlier `system` arg to nixosSystem make its way here?
hostPlatform.system = target;
};
# nixpkgs.buildPlatform = local; # set by instantiate.nix instead
# nixpkgs.config.replaceStdenv = { pkgs }: pkgs.ccacheStdenv;
}
(import ./hosts/instantiate.nix { hostName = name; })
self.nixosModules.default
self.nixosModules.passthru
{
nixpkgs.overlays = [
self.overlays.passthru
self.overlays.sane-all
];
}
];
};
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;
# localSystem = local;
# crossSystem = 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).
@@ -184,30 +114,27 @@
# - 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
hostPkgs = mapAttrValues (host: host.config.system.build.pkgs) self.nixosConfigurations;
hostPrograms = mapAttrValues (host: mapAttrValues (p: p.package) host.config.sane.programs) self.nixosConfigurations;
host-pkgs = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.pkgs) 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;
preferences = final: prev: import ./overlays/preferences.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 (
next: prev: {
stable = inputs.nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
}
) else (next: prev: {});
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
uninsane = uninsane-dot-org.overlay;
in
(mobile final prev)
// (uninsane final prev)
;
next: prev:
(stable next prev) // (mobile next prev) // (uninsane next prev);
};
nixosModules = rec {
@@ -231,259 +158,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";
sanePkgs = import ./pkgs { inherit pkgs; };
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
nix build '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} $@
sudo nix sign-paths -r -k /run/secrets/nix_serve_privkey $(readlink ./result-${host})
# XXX: this triggers another config eval & (potentially) build.
# if the config changed between these invocations, the above signatures might not apply to the deployed config.
# let the user handle that edge case by re-running this whole command
nixos-rebuild --flake '.#${host}' ${action} --target-host colin@${addr} --use-remote-sudo $@
'';
# pkg updating.
# a cleaner alternative lives here: <https://discourse.nixos.org/t/how-can-i-run-the-updatescript-of-personal-packages/25274/2>
mkUpdater = attrPath: {
type = "app";
program = let
pkg = pkgs.lib.getAttrFromPath attrPath sanePkgs;
strAttrPath = pkgs.lib.concatStringsSep "." attrPath;
commandArgv = pkg.updateScript.command or pkg.updateScript;
command = pkgs.lib.escapeShellArgs commandArgv;
in builtins.toString (pkgs.writeShellScript "update-${strAttrPath}" ''
export UPDATE_NIX_NAME=${pkg.name}
export UPDATE_NIX_PNAME=${pkg.pname}
export UPDATE_NIX_OLD_VERSION=${pkg.version}
export UPDATE_NIX_ATTR_PATH=${strAttrPath}
${command}
'');
};
mkUpdatersNoAliases = opts: basePath: pkgs.lib.concatMapAttrs
(name: pkg:
if pkg.recurseForDerivations or false then {
"${name}" = mkUpdaters opts (basePath ++ [ name ]);
} else if pkg.updateScript or null != null then {
"${name}" = mkUpdater (basePath ++ [ name ]);
} else {}
)
(pkgs.lib.getAttrFromPath basePath sanePkgs);
mkUpdaters = { ignore ? [] }@opts: basePath:
let
updaters = mkUpdatersNoAliases opts basePath;
invokeUpdater = name: pkg:
let
fullPath = basePath ++ [ name ];
doUpdateByDefault = !builtins.elem fullPath ignore;
# in case `name` has a `.` in it, we have to quote it
escapedPath = builtins.map (p: ''"${p}"'') fullPath;
updatePath = builtins.concatStringsSep "." ([ "update" "pkgs" ] ++ escapedPath);
in pkgs.lib.optionalString doUpdateByDefault (
pkgs.lib.escapeShellArgs [
"nix" "run" ".#${updatePath}"
]
);
in {
type = "app";
program = builtins.toString (pkgs.writeShellScript
(builtins.concatStringsSep "-" (["update"] ++ basePath))
(builtins.concatStringsSep
"\n"
(pkgs.lib.mapAttrsToList invokeUpdater updaters)
)
);
} // updaters;
in {
help = {
update-feeds = {
type = "app";
program = let
helpMsg = builtins.toFile "nixos-config-help-message" ''
commands:
- `nix run '.#help'`
- show this message
- `nix run '.#update.pkgs'`
- updates every package
- `nix run '.#update.feeds'`
- updates metadata for all feeds
- `nix run '.#init-feed' <url>`
- `nix run '.#deploy-{lappy,moby,moby-test,servo}' [nixos-rebuild args ...]`
- `nix run '.#check'`
- make sure all systems build; NUR evaluates
specific build targets of interest:
- `nix build '.#imgs.rescue'`
'';
in builtins.toString (pkgs.writeShellScript "nixos-config-help" ''
cat ${helpMsg}
echo ""
echo "complete flake structure:"
nix flake show --option allow-import-from-derivation true
'');
program = "${pkgs.feeds.passthru.updateScript}";
};
update.pkgs = mkUpdaters { ignore = [ ["feeds"] ]; } [];
update.feeds = mkUpdaters {} [ "feeds" ];
init-feed = {
# use like `nix run '.#init-feed' uninsane.org`
type = "app";
program = "${pkgs.feeds.init-feed}";
};
deploy-lappy = {
type = "app";
program = ''${deployScript "lappy" "lappy" "switch"}'';
};
deploy-moby-test = {
type = "app";
program = ''${deployScript "moby" "moby-hn" "test"}'';
};
deploy-moby = {
type = "app";
program = ''${deployScript "moby" "moby-hn" "switch"}'';
};
deploy-servo = {
type = "app";
program = ''${deployScript "servo" "servo" "switch"}'';
};
sync-moby = {
# copy music from the current device to moby
# TODO: should i actually sync from /mnt/servo-media/Music instead of the local drive?
type = "app";
program = builtins.toString (pkgs.writeShellScript "sync-to-moby" ''
sudo mount /mnt/moby-home
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music ~/Music /mnt/moby-home/Music
'');
};
sync-lappy = {
# copy music from servo to lappy
# can run this from any device that has ssh access to lappy
type = "app";
program = builtins.toString (pkgs.writeShellScript "sync-to-lappy" ''
sudo mount /mnt/lappy-home
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music /mnt/servo-media/Music /mnt/lappy-home/Music
'');
};
check = {
type = "app";
program = builtins.toString (pkgs.writeShellScript "check-all" ''
nix run '.#check.nur'
RC0=$?
nix run '.#check.host-configs'
RC1=$?
nix run '.#check.rescue'
RC2=$?
echo "nur: $RC0"
echo "host-configs: $RC1"
echo "rescue: $RC2"
exit $(($RC0 | $RC1 | $RC2))
'');
};
check.nur = {
# `nix run '.#check-nur'`
# validates that my repo can be included in the Nix User Repository
type = "app";
program = builtins.toString (pkgs.writeShellScript "check-nur" ''
cd ${./.}/integrations/nur
NIX_PATH= NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix-env -f . -qa \* --meta --xml \
--allowed-uris https://static.rust-lang.org \
--option restrict-eval true \
--option allow-import-from-derivation true \
--drv-path --show-trace \
-I nixpkgs=$(nix-instantiate --find-file nixpkgs) \
-I ../../ \
| tee # tee to prevent interactive mode
'');
};
check.host-configs = {
type = "app";
program = let
checkHost = host: ''
nix build -v '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} -j2 $@
RC_${host}=$?
'';
in builtins.toString (pkgs.writeShellScript
"check-host-configs"
''
${checkHost "desko"}
${checkHost "lappy"}
${checkHost "servo"}
${checkHost "moby"}
${checkHost "rescue"}
echo "desko: $RC_desko"
echo "lappy: $RC_lappy"
echo "servo: $RC_servo"
echo "moby: $RC_moby"
echo "rescue: $RC_rescue"
exit $(($RC_desko | $RC_lappy | $RC_servo | $RC_moby | $RC_rescue))
''
);
};
check.rescue = {
type = "app";
program = builtins.toString (pkgs.writeShellScript "check-rescue" ''
nix build -v '.#imgs.rescue' --out-link ./result-rescue-img -j2
'');
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,29 +4,17 @@
./fs.nix
];
# sane.guest.enable = true;
# services.distccd.enable = true;
# sane.programs.distcc.enableFor.user.guest = true;
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.steam.enableFor.user.colin = true;
sane.programs.guiApps.suggestedPrograms = [ "desktopGuiApps" ];
sane.programs.consoleUtils.suggestedPrograms = [ "consoleMediaUtils" "desktopConsoleUtils" ];
# sane.programs.devPkgs.enableFor.user.colin = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
@@ -34,9 +22,13 @@
# 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;
systemd.services.wpa_supplicant.enable = false;
# default config: https://man.archlinux.org/man/snapper-configs.5
# defaults to something like:
@@ -45,11 +37,28 @@
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 = {
# 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
};
sane.user.persist.plaintext = [
".steam"
".local/share/Steam"
];
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
system.stateVersion = "21.05";
}

View File

@@ -2,10 +2,17 @@
{
sane.persist.root-on-tmpfs = true;
# increase /tmp space (defaults to 50% of RAM) for building large nix things.
# we need a /tmp for building large nix things.
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
fileSystems."/tmp".options = [ "size=64G" ];
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=777"
"size=64G"
"defaults"
];
};
fileSystems."/nix" = {
# device = "/dev/disk/by-uuid/985a0a32-da52-4043-9df7-615adec2e4ff";
device = "/dev/disk/by-uuid/0ab0770b-7734-4167-88d9-6e4e20bb2a56";

View File

@@ -2,26 +2,25 @@
{
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"
];
sane.programs.consoleUtils.suggestedPrograms = [ "consoleMediaUtils" "desktopConsoleUtils" ];
sane.programs.guiApps.suggestedPrograms = [ "desktopGuiApps" ];
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:
@@ -30,10 +29,12 @@
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
# services.ipfs.enable = true;
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
system.stateVersion = "21.05";
}

View File

@@ -2,6 +2,15 @@
{
sane.persist.root-on-tmpfs = true;
# we need a /tmp of default size (half RAM) for building large nix things
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=777"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/75230e56-2c69-4e41-b03e-68475f119980";

View File

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

View File

@@ -1,7 +0,0 @@
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+inet(evdev)" };
xkb_geometry { include "pc(pc105)" };
};

View File

@@ -1,115 +1,76 @@
# Pinephone
# other setups to reference:
# - <https://hamblingreen.gitlab.io/2022/03/02/my-pinephone-setup.html>
# - sxmo Arch user. lots of app recommendations
#
# wikis, resources, ...:
# - Linux Phone Apps: <https://linuxphoneapps.org/>
# - massive mobile-friendly app database
# - Mobian wiki: <https://wiki.mobian-project.org/doku.php?id=start>
# - recommended apps, chatrooms
{ config, pkgs, lib, ... }:
{
imports = [
./bootloader.nix
./firmware.nix
./fs.nix
./gps.nix
./kernel.nix
./polyfill.nix
];
sane.roles.client = true;
sane.zsh.showDeadlines = false; # unlikely to act on them when in shell
sane.services.wg-home.enable = true;
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
# 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.gui.sxmo.enable = true;
sane.programs.guiApps.suggestedPrograms = [ "handheldGuiApps" ];
# sane.programs.consoleUtils.enableFor.user.colin = false;
# sane.programs.guiApps.enableFor.user.colin = false;
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
sane.programs.mercurial.enableFor.user.colin = false; # does not cross compile
sane.programs.sequoia.enableFor.user.colin = false;
sane.programs.tuiApps.enableFor.user.colin = false; # visidata, others, don't compile well
# disabled for faster deploys
sane.programs.soundconverter.enableFor.user.colin = false;
sane.web-browser = {
# compromise impermanence for the sake of usability
persistCache = "private";
persistData = "private";
# enabled for easier debugging
sane.programs.eg25-control.enableFor.user.colin = true;
sane.programs.rtl8723cs-wowlan.enableFor.user.colin = true;
# i don't do crypto stuff on moby
addons.ether-metamask.enable = false;
# addons.sideberry.enable = false;
};
# sane.programs.ntfy-sh.config.autostart = true;
sane.programs.dino.config.autostart = true;
# sane.programs.calls.config.autostart = true;
sane.programs.mpv.config.vo = "wlshm"; #< see hosts/common/programs/mpv.nix for details
sane.user.persist.plaintext = [
".config/pulse" # persist pulseaudio volume
];
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
sane.programs.firefox.env = lib.mkForce {};
sane.programs.epiphany.env.BROWSER = "epiphany";
sane.programs.firefox.enableFor.user.colin = false; # use epiphany instead
# note the .conf.d approach: using ~/.config/pipewire/pipewire.conf directly breaks all audio,
# presumably because that deletes the defaults entirely whereas the .conf.d approach selectively overrides defaults
sane.user.fs.".config/pipewire/pipewire.conf.d/10-fix-dino-mic-cutout.conf".symlink.text = ''
# config docs: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire#properties>
# useful to run `pw-top` to see that these settings are actually having effect,
# and `pw-metadata` to see if any settings conflict (e.g. max-quantum < min-quantum)
#
# restart pipewire after editing these files:
# - `systemctl --user restart pipewire`
# - pipewire users will likely stop outputting audio until they are also restarted
#
# there's seemingly two buffers for the mic (see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>)
# 1. Pipewire buffering out of the driver and into its own member.
# 2. Pipewire buffering into Dino.
# the latter is fixed at 10ms by Dino, difficult to override via runtime config.
# the former defaults low (e.g. 512 samples)
# this default configuration causes the mic to regularly drop out entirely for a couple seconds at a time during a call,
# presumably because the system can't keep up (pw-top shows incrementing counter in ERR column).
# `pw-metadata -n settings 0 clock.force-quantum 1024` reduces to about 1 error per second.
# `pw-metadata -n settings 0 clock.force-quantum 2048` reduces to 1 error every < 10s.
# pipewire default config includes `clock.power-of-two-quantum = true`
context.properties = {
default.clock.min-quantum = 2048
default.clock.max-quantum = 8192
}
'';
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.
# even 10 can be too much
# TODO: compress moby kernels!
boot.loader.generic-extlinux-compatible.configurationLimit = 8;
# mobile.bootloader.enable = false;
# mobile.boot.stage-1.enable = false;
# boot.initrd.systemd.enable = false;
# boot.initrd.services.swraid.enable = false; # attempt to fix dm_mod stuff
# disable proximity sensor.
# the filtering/calibration is bad that it causes the screen to go fully dark at times.
boot.blacklistedKernelModules = [ "stk3310" ];
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
# this is because they can't allocate enough video ram.
# the default CMA seems to be 32M.
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep: maybe a memory leak?
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
boot.kernelParams = [ "cma=512M" ];
# hardware.firmware makes the referenced files visible to the kernel, for whenever a driver explicitly asks for them.
# these files are visible from userspace by following `/sys/module/firmware_class/parameters/path`
#
# mobile-nixos' /lib/firmware includes:
# rtl_bt (bluetooth)
# anx7688-fw.bin (USB-C chip: power negotiation, HDMI/dock)
# anx7688-fw.bin (USB-C -> HDMI bridge)
# ov5640_af.bin (camera module)
# hardware.firmware = [ config.mobile.device.firmware ];
# hardware.firmware = [ pkgs.rtl8723cs-firmware ];
hardware.firmware = [
(pkgs.linux-firmware-megous.override {
# rtl_bt = false probably means no bluetooth connectivity.
# N.B.: DON'T RE-ENABLE without first confirming that wake-on-lan works during suspend (rtcwake).
# it seems the rtl_bt stuff ("bluetooth coexist") might make wake-on-LAN radically more flaky.
rtl_bt = false;
})
];
hardware.firmware = [ pkgs.rtl8723cs-firmware ];
system.stateVersion = "21.11";
@@ -122,53 +83,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.override {
# internal speaker has a tendency to break :(
preferEarpiece = true;
})
];
systemd = let
ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
in {
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
# 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}";
# pipewire
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
# pulseaudio
# user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
# services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
# TODO: move elsewhere...
services.ModemManager.serviceConfig = {
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
# --debug sets DEBUG level logging: so reset
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
};
};
services.udev.extraRules = let
chmod = "${pkgs.coreutils}/bin/chmod";
chown = "${pkgs.coreutils}/bin/chown";
in ''
# make Pinephone flashlight writable by user.
# taken from postmarketOS: <repo:postmarketOS/pmaports:device/main/device-pine64-pinephone/60-flashlight.rules>
SUBSYSTEM=="leds", DEVPATH=="*/*:flash", RUN+="${chmod} g+w /sys%p/brightness /sys%p/flash_strobe", RUN+="${chown} :video /sys%p/brightness /sys%p/flash_strobe"
# make Pinephone front LEDs writable by user.
SUBSYSTEM=="leds", DEVPATH=="*/*:indicator", RUN+="${chmod} g+w /sys%p/brightness", RUN+="${chown} :video /sys%p/brightness"
'';
hardware.opengl.driSupport = true;
}

View File

@@ -1,69 +0,0 @@
# pinephone GPS happens in EG25 modem
# serial control interface to modem is /dev/ttyUSB2
# after enabling GPS, readout is /dev/ttyUSB1
#
# minimal process to enable modem and GPS:
# - `echo 1 > /sys/class/modem-power/modem-power/device/powered`
# - `screen /dev/ttyUSB2 115200`
# - `AT+QGPSCFG="nmeasrc",1`
# - `AT+QGPS=1`
# this process is automated by my `eg25-control` program and services (`eg25-control-powered`, `eg25-control-gps`)
# - see the `modules/` directory further up this repository.
#
# now, something like `gpsd` can directly read from /dev/ttyUSB1,
# or geoclue can query the GPS directly through modem-manager
#
# initial GPS fix can take 15+ minutes.
# meanwhile, services like eg25-manager or eg25-control-freshen-agps can speed this up by uploading assisted GPS data to the modem.
#
# support/help:
# - geoclue, gnome-maps
# - irc: #gnome-maps on irc.gimp.org
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
#
# programs to pair this with:
# - `satellite-gtk`: <https://codeberg.org/tpikonen/satellite>
# - shows/tracks which satellites the GPS is connected to; useful to understand fix characteristics
# - `gnome-maps`: uses geoclue, has route planning
# - `mepo`: uses gpsd, minimalist, flaky, and buttons are kinda hard to activate on mobile
# - puremaps?
# - osmin?
#
# known/outstanding bugs:
# - `systemctl start eg25-control-gps` can the hang the whole system (2023/10/06)
# - i think it's actually `eg25-control-powered` which does this (started by the gps)
# - best guess is modem draws so much power at launch that other parts of the system see undervoltage
# - workaround is to hard power-cycle the system. the modem may not bring up after reboot: leave unpowered for 60s and boot again.
#
# future work:
# - integrate with [wigle](https://www.wigle.net/) for offline equivalent to Mozilla Location Services
{ config, lib, ... }:
{
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
# ^ should return <lat> <long>
services.gpsd.enable = true;
services.gpsd.devices = [ "/dev/ttyUSB1" ];
# test geoclue2 by building `geoclue2-with-demo-agent`
# and running "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
# note that geoclue is dbus-activated, and auto-stops after 60s with no caller
services.geoclue2.enable = true;
services.geoclue2.appConfig.where-am-i = {
# this is the default "agent", shipped by geoclue package: allow it to use location
isAllowed = true;
isSystem = false;
# XXX: setting users != [] might be causing `where-am-i` to time out
users = [
# restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
(builtins.toString config.users.users.colin.uid)
];
};
systemd.services.geoclue.after = lib.mkForce []; #< defaults to network-online, but not all my sources require network
users.users.geoclue.extraGroups = [
"dialout" # TODO: figure out if dialout is required. that's for /dev/ttyUSB1, but geoclue probably doesn't read that?
];
sane.services.eg25-control.enable = true;
sane.programs.where-am-i.enableFor.user.colin = true;
}

View File

@@ -1,89 +1,146 @@
{ pkgs, ... }:
{ lib, pkgs, ... }:
let
dmesg = "${pkgs.util-linux}/bin/dmesg";
grep = "${pkgs.gnugrep}/bin/grep";
modprobe = "${pkgs.kmod}/bin/modprobe";
ensureHWReady = ''
# common boot failure:
# blank screen (no backlight even), with the following log:
# ```syslog
# sun8i-dw-hdmi 1ee0000.hdmi: Couldn't get the HDMI PHY
# ...
# sun4i-drm display-engine: Couldn't bind all pipelines components
# ...
# sun8i-dw-hdmi: probe of 1ee0000.hdmi failed with error -17
# ```
#
# in particular, that `probe ... failed` occurs *only* on failed boots
# (the other messages might sometimes occur even on successful runs?)
#
# reloading the sun8i hdmi driver usually gets the screen on, showing boot text.
# then restarting display-manager.service gets us to the login.
#
# NB: the above log is default level. though less specific, there's a `err` level message that also signals this:
# sun4i-drm display-engine: failed to bind 1ee0000.hdmi (ops sun8i_dw_hdmi_ops [sun8i_drm_hdmi]): -17
# NB: this is the most common, but not the only, failure mode for `display-manager`.
# another error seems characterized by these dmesg logs, in which reprobing sun8i_drm_hdmi does not fix:
# ```syslog
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't get the MIPI D-PHY
# sun4i-drm display-engine: Couldn't bind all pipelines components
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't register our component
# ```
# use the last commit on the 5.18 branch (5.18.14)
# manjaro's changes between kernel patch versions tend to be minimal if any.
manjaroBase = "https://gitlab.manjaro.org/manjaro-arm/packages/core/linux/-/raw/25bd828cd47b1c6e09fcbcf394a649b89d2876dd";
manjaroPatch = name: sha256: {
inherit name;
patch = pkgs.fetchpatch {
inherit name;
url = "${manjaroBase}/${name}?inline=false";
inherit sha256;
};
};
if (${dmesg} --kernel --level err --color=never --notime | ${grep} -q 'sun4i-drm display-engine: failed to bind 1ee0000.hdmi')
then
echo "reprobing sun8i_drm_hdmi"
# if a command here fails it errors the whole service, so prefer to log instead
${modprobe} -r sun8i_drm_hdmi || echo "failed to unload sun8i_drm_hdmi"
${modprobe} sun8i_drm_hdmi || echo "failed to load sub8i_drm_hdmi"
fi
'';
# the idea for patching off Manjaro's kernel comes from jakewaksbaum:
# - https://git.sr.ht/~jakewaksbaum/pi/tree/af20aae5653545d6e67a459b59ee3e1ca8a680b0/item/kernel/default.nix
# - he later abandoned this, i think because he's using the Pinephone Pro which received mainline support.
manjaroPatches = [
(manjaroPatch
"1001-arm64-dts-allwinner-add-hdmi-sound-to-pine-devices.patch"
"sha256-DApd791A+AxB28Ven/MVAyuyVphdo8KQDx8O7oxVPnc="
)
# these patches below are critical to enable wifi (RTL8723CS)
# - the alternative is a wholly forked kernel by megi/megous:
# - https://xnux.eu/howtos/build-pinephone-kernel.html#toc-how-to-build-megi-s-pinehpone-kernel
# - i don't know if these patches are based on megi's or original
(manjaroPatch
"2001-Bluetooth-Add-new-quirk-for-broken-local-ext-features.patch"
"sha256-CExhJuUWivegxPdnzKINEsKrMFx/m/1kOZFmlZ2SEOc="
)
(manjaroPatch
"2002-Bluetooth-btrtl-add-support-for-the-RTL8723CS.patch"
"sha256-dDdvOphTcP/Aog93HyH+L9m55laTgtjndPSE4/rnzUA="
)
(manjaroPatch
"2004-arm64-dts-allwinner-enable-bluetooth-pinetab-pinepho.patch"
"sha256-o43P3WzXyHK1PF+Kdter4asuyGAEKO6wf5ixcco2kCQ="
)
# XXX: this one has a Makefile, which hardcodes /sbin/depmod:
# - drivers/staging/rtl8723cs/Makefile
# - not sure if this is problematic?
(manjaroPatch
"2005-staging-add-rtl8723cs-driver.patch"
"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:
# even with the Manjaro patches, stock 5.18 has a few issues on Pinephone:
# - no battery charging
# - 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.linuxPackagesFor pkgs.linux-manjaro;
# boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux_latest;
# alternatively, apply patches directly to stock nixos kernel:
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:
# boot.kernelPackages =
# let p = (import nixpkgs { localSystem = "x86_64-linux"; });
# in p.pkgsCross.aarch64-multiplatform.linuxPackages_5_18;
# # non-cross:
# # boot.kernelPackages = pkgs.linuxPackages_5_18;
# boot.kernelPatches = manjaroPatches ++ [
# (patchDefconfig kernelConfig)
# ];
# configure nixos to build a compressed kernel image, since it doesn't usually do that for aarch64 target.
# without this i run out of /boot space in < 10 generations
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 :-(
};
# disable proximity sensor.
# the filtering/calibration is bad that it causes the screen to go fully dark at times.
boot.blacklistedKernelModules = [ "stk3310" ];
boot.kernelParams = [
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
# this is because they can't allocate enough video ram.
# see related nixpkgs issue: <https://github.com/NixOS/nixpkgs/issues/260222>
#
# the default CMA seems to be 32M.
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep (phosh): maybe a memory leak?
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
"cma=512M"
# 2023/10/20: potential fix for the lima (GPU) timeout bugs:
# - <https://gitlab.com/postmarketOS/pmaports/-/issues/805#note_890467824>
"lima.sched_timeout_ms=2000"
];
services.xserver.displayManager.job.preStart = ensureHWReady;
systemd.services.greetd.preStart = ensureHWReady;
}

View File

@@ -1,96 +0,0 @@
# this file configures preferences per program, without actually enabling any programs.
# the goal is to separate the place where we decide *what* to use (i.e. `sane.programs.firefox.enable = true` -- at the toplevel)
# from where we specific how that thing should behave *if* it's in use.
#
# NixOS backgrounds:
# - <https://github.com/NixOS/nixos-artwork>
# - <https://github.com/NixOS/nixos-artwork/issues/50> (colorful; unmerged)
# - <https://github.com/NixOS/nixos-artwork/pull/60/files> (desktop-oriented; clean; unmerged)
# - <https://itsfoss.com/content/images/2023/04/nixos-tutorials.png>
{ lib, pkgs, sane-lib, ... }:
{
sane.programs.firefox.config = {
# compromise impermanence for the sake of usability
persistCache = "private";
persistData = "private";
# i don't do crypto stuff on moby
addons.ether-metamask.enable = false;
# sidebery UX doesn't make sense on small screen
addons.sidebery.enable = false;
};
sane.programs.swaynotificationcenter.config = {
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
};
sane.gui.sxmo = {
nogesture = true;
settings = {
### hardware: touch screen
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-path/platform-1c2ac00.i2c-event";
# vol and power are detected correctly by upstream
### preferences
DEFAULT_COUNTRY = "US";
SXMO_AUTOROTATE = "1"; # enable auto-rotation at launch. has no meaning in stock/upstream sxmo-utils
# BEMENU lines (wayland DMENU):
# - camera is 9th entry
# - flashlight is 10th entry
# - config is 14th entry. inside that:
# - autorotate is 11th entry
# - system menu is 19th entry
# - close is 20th entry
# - power is 15th entry
# - close is 16th entry
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
# gravity: how far to tilt the device before the screen rotates
# for a given setting, normal <-> invert requires more movement then left <-> right
# i.e. the settingd doesn't feel completely symmetric
# SXMO_ROTATION_GRAVITY default is 16374
# SXMO_ROTATION_GRAVITY = "12800"; # uncomfortably high
# SXMO_ROTATION_GRAVITY = "12500"; # kinda uncomfortable when walking
SXMO_ROTATION_GRAVITY = "12000";
SXMO_SCREENSHOT_DIR = "/home/colin/Pictures"; # default: "$HOME"
# sway/wayland scaling:
# - conflicting info out there on how scaling actually works
# at the least, for things where it matters (mpv), it seems like scale settings have 0 effect on perf
# ways to enforce scaling:
# - <https://wiki.archlinux.org/title/HiDPI>
# - `swaymsg -- output DSI-1 scale 2.0` (scales everything)
# - `dconf write /org/gnome/desktop/interface/text-scaling-factor 2.0` (scales ONLY TEXT)
# - `GDK_DPI_SCALE=2.0` (scales ONLY TEXT)
#
# application notes:
# - cozy: in landscape, playback position is not visible unless scale <= 1.7
# - if in a tab, then scale 1.6 is the max
# SXMO_SWAY_SCALE = "1.5"; # hard to press gPodder icons
SXMO_SWAY_SCALE = "1.6";
# SXMO_SWAY_SCALE = "1.8";
# SXMO_SWAY_SCALE = "2";
SXMO_WORKSPACE_WRAPPING = "5"; # how many workspaces. default: 4
# wvkbd layers:
# - full
# - landscape
# - special (e.g. coding symbols like ~)
# - emoji
# - nav
# - simple (like landscape, but no parens/tab/etc; even fewer chars)
# - simplegrid (simple, but grid layout)
# - dialer (digits)
# - cyrillic
# - arabic
# - persian
# - greek
# - georgian
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
WVKBD_LAYERS = "full,special,emoji";
};
};
}

View File

@@ -1,17 +1,12 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
imports = [
./fs.nix
];
boot.loader.generic-extlinux-compatible.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.persist.enable = false;
sane.nixcache.enable = false; # don't want to be calling out to dead machines that we're *trying* to rescue
# auto-login at shell
services.getty.autologinUser = "colin";
# users.users.colin.initialPassword = "colin";
# 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,22 +15,10 @@
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.programs.consoleUtils.suggestedPrograms = [
"desktopConsoleUtils"
"sane-scripts.stop-all-servo"
];
sane.persist.enable = true;
sane.services.dyn-dns.enable = true;
sane.services.wg-home.enable = true;
sane.services.wg-home.visibleToWan = true;
sane.services.wg-home.forwardToWan = true;
sane.services.wg-home.routeThroughServo = false;
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
sane.nixcache.substituters.servo = false;
sane.nixcache.substituters.desko = false;
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
# automatically log in at the virtual consoles.

View File

@@ -2,9 +2,15 @@
{
sane.persist.root-on-tmpfs = true;
# increase /tmp space (defaults to 50% of RAM) for building large nix things.
# even the stock `nixpkgs.linux` consumes > 16 GB of tmp
fileSystems."/tmp".options = [ "size=32G" ];
# we need a /tmp for building large nix things
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=777"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
@@ -36,60 +42,31 @@
};
sane.fs."/mnt/persist/ext".mount = {};
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: this is overly broad; only need media and share directories to be persisted
{ user = "colin"; group = "users"; path = "/var/lib/uninsane"; }
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
];
# force some problematic directories to always get correct permissions:
sane.fs."/var/lib/uninsane/media".dir.acl = {
user = "colin"; group = "media"; mode = "0775";
};
sane.fs."/var/lib/uninsane/media/archive".dir = {};
sane.fs."/var/lib/uninsane/media/archive/README.md".file.text = ''
this directory is for media i wish to remove from my library,
but keep for a short time in case i reverse my decision.
treat it like a system trash can.
'';
sane.fs."/var/lib/uninsane/media/Books".dir = {};
sane.fs."/var/lib/uninsane/media/Books/Audiobooks".dir = {};
sane.fs."/var/lib/uninsane/media/Books/Books".dir = {};
sane.fs."/var/lib/uninsane/media/Books/Visual".dir = {};
sane.fs."/var/lib/uninsane/media/collections".dir = {};
sane.fs."/var/lib/uninsane/media/datasets".dir = {};
sane.fs."/var/lib/uninsane/media/freeleech".dir = {};
sane.fs."/var/lib/uninsane/media/Music".dir = {};
sane.fs."/var/lib/uninsane/media/Pictures".dir = {};
sane.fs."/var/lib/uninsane/media/Videos".dir = {};
sane.fs."/var/lib/uninsane/media/Videos/Film".dir = {};
sane.fs."/var/lib/uninsane/media/Videos/Shows".dir = {};
sane.fs."/var/lib/uninsane/media/Videos/Talks".dir = {};
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
this directory may seem redundant with ../media/datasets. it isn't.
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
the contents should be a subset of what's in ../media/datasets.
'';
# make sure large media is stored to the HDD
sane.persist.sys.ext = [
{
user = "colin";
group = "users";
mode = "0777";
path = "/var/lib/uninsane/media/Videos";
directory = "/var/lib/uninsane/media/Videos";
}
{
user = "colin";
group = "users";
mode = "0777";
path = "/var/lib/uninsane/media/freeleech";
}
{
user = "colin";
group = "users";
mode = "0777";
path = "/var/lib/uninsane/media/datasets";
directory = "/var/lib/uninsane/media/freeleech";
}
];
# in-memory compressed RAM (seems to be dynamically sized)
# zramSwap = {
# enable = true;
# };
# btrfs doesn't easily support swapfiles
# swapDevices = [
# { device = "/nix/persist/swapfile"; size = 4096; }

View File

@@ -1,233 +1,209 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
let
portOpts = with lib; types.submodule {
options = {
visibleTo.ovpn = mkOption {
type = types.bool;
default = false;
};
};
};
in
{
options = with lib; {
sane.ports.ports = mkOption {
# add the `visibleTo.ovpn` option
type = types.attrsOf portOpts;
};
};
networking.domain = "uninsane.org";
config = {
networking.domain = "uninsane.org";
# 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.
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
# 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;
sane.ports.openFirewall = true;
sane.ports.openUpnp = true;
# networking.firewall.enable = false;
networking.firewall.enable = true;
# view refused packets with: `sudo journalctl -k`
# networking.firewall.logRefusedPackets = true;
# this is needed to forward packets from the VPN to the host
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# 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.
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
# 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;
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
# networking.nameservers = [
# "1.1.1.1"
# "9.9.9.9"
# ];
# this is needed to forward packets from the VPN to the host
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# use systemd's stub resolver.
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
# in the ovnps namespace to use the provider's DNS resolvers.
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
# there also seems to be some cache somewhere that's shared between the two namespaces.
# i think this is a libc thing. might need to leverage proper cgroups to _really_ kill it.
# - 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;
networking.nameservers = [
# use systemd-resolved resolver
# full resolver (which understands /etc/hosts) lives on 127.0.0.53
# stub resolver (just forwards upstream) lives on 127.0.0.54
"127.0.0.53"
];
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
# networking.nameservers = [
# "1.1.1.1"
# "9.9.9.9"
# ];
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
# in a way that's unaware of my VPN routing, so routes are frequently poor against
# services which advertise different IPs based on geolocation.
# nscd claims to be usable without a cache, but in practice i can't get it to not cache!
# nsncd is the Name Service NON-Caching Daemon. it's a drop-in that doesn't cache;
# this is OK on the host -- because systemd-resolved caches. it's probably sub-optimal
# in the netns and we query upstream DNS more often than needed. hm.
# TODO: run a separate recursive resolver in each namespace.
services.nscd.enableNsncd = true;
# use systemd's stub resolver.
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
# in the ovnps namespace to use the provider's DNS resolvers.
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
# there also seems to be some cache somewhere that's shared between the two namespaces.
# i think this is a libc thing. might need to leverage proper cgroups to _really_ kill it.
# - 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
# stub resolver (just forwards upstream) lives on 127.0.0.54
"127.0.0.53"
# services.resolved.extraConfig = ''
# # docs: `man resolved.conf`
# # DNS servers to use via the `wg-ovpns` interface.
# # i hope that from the root ns, these aren't visible.
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
# FallbackDNS=1.1.1.1 9.9.9.9
# '';
# OVPN CONFIG (https://www.ovpn.com):
# DOCS: https://nixos.wiki/wiki/WireGuard
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
# TODO: why not create the namespace as a seperate operation (nix config for that?)
networking.wireguard.enable = true;
networking.wireguard.interfaces.wg-ovpns = let
ip = "${pkgs.iproute2}/bin/ip";
in-ns = "${ip} netns exec ovpns";
iptables = "${pkgs.iptables}/bin/iptables";
veth-host-ip = "10.0.1.5";
veth-local-ip = "10.0.1.6";
vpn-ip = "185.157.162.178";
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
vpn-dns = "46.227.67.134";
in {
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
# wg is active only in this namespace.
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
# sudo ip netns exec ovpns ping www.google.com
interfaceNamespace = "ovpns";
ips = [
"185.157.162.178/32"
];
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
# in a way that's unaware of my VPN routing, so routes are frequently poor against
# services which advertise different IPs based on geolocation.
# nscd claims to be usable without a cache, but in practice i can't get it to not cache!
# nsncd is the Name Service NON-Caching Daemon. it's a drop-in that doesn't cache;
# this is OK on the host -- because systemd-resolved caches. it's probably sub-optimal
# in the netns and we query upstream DNS more often than needed. hm.
# TODO: run a separate recursive resolver in each namespace.
services.nscd.enableNsncd = true;
# services.resolved.extraConfig = ''
# # docs: `man resolved.conf`
# # DNS servers to use via the `wg-ovpns` interface.
# # i hope that from the root ns, these aren't visible.
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
# FallbackDNS=1.1.1.1 9.9.9.9
# '';
# OVPN CONFIG (https://www.ovpn.com):
# DOCS: https://nixos.wiki/wiki/WireGuard
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
# TODO: why not create the namespace as a seperate operation (nix config for that?)
networking.wireguard.enable = true;
networking.wireguard.interfaces.wg-ovpns = let
ip = "${pkgs.iproute2}/bin/ip";
in-ns = "${ip} netns exec ovpns";
iptables = "${pkgs.iptables}/bin/iptables";
veth-host-ip = "10.0.1.5";
veth-local-ip = "10.0.1.6";
vpn-ip = "185.157.162.178";
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
vpn-dns = "46.227.67.134";
bridgePort = port: proto: ''
${in-ns} ${iptables} -A PREROUTING -t nat -p ${proto} --dport ${port} -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}
'';
bridgeStatements = lib.foldlAttrs
(acc: port: portCfg: acc ++ (builtins.map (bridgePort port) portCfg.protocol))
[]
config.sane.ports.ports;
in {
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
# wg is active only in this namespace.
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
# sudo ip netns exec ovpns ping www.google.com
interfaceNamespace = "ovpns";
ips = [
"185.157.162.178/32"
];
peers = [
{
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
endpoint = "185.157.162.10:9930";
# alternatively: use hostname, but that presents bootstrapping issues (e.g. if host net flakes)
# endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
allowedIPs = [ "0.0.0.0/0" ];
# nixOS says this is important for keeping NATs active
persistentKeepalive = 25;
# re-executes wg this often. docs hint that this might help wg notice DNS/hostname changes.
# so, maybe that helps if we specify endpoint as a domain name
# dynamicEndpointRefreshSeconds = 30;
# when refresh fails, try it again after this period instead.
# TODO: not avail until nixpkgs upgrade
# dynamicEndpointRefreshRestartSeconds = 5;
}
];
preSetup = ''
${ip} netns add ovpns || echo "ovpns already exists"
'';
postShutdown = ''
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
${ip} netns delete ovpns || echo "couldn't delete ovpns"
# restore rules/routes
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
${ip} rule add from all lookup local pref 0
${ip} rule del from all lookup local pref 100
'';
postSetup = ''
# DOCS:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
${ip} link set ovpns-veth-a up
# mv veth-b into the ovpns namespace
${ip} link set ovpns-veth-b netns ovpns
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
${in-ns} ip link set ovpns-veth-b up
# make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination.
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
# for traffic originating at the host veth to the WAN, use the veth as our gateway
# not sure if the metric 1002 matters.
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
# give the default route lower priority
${ip} rule add from all lookup local pref 100
${ip} rule del from all lookup local pref 0
# 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.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${vpn-dns}:53
'' + (lib.concatStringsSep "\n" bridgeStatements);
};
# create a new routing table that we can use to proxy traffic out of the root namespace
# through the ovpns namespace, and to the WAN via VPN.
networking.iproute2.rttablesExtraConfig = ''
5 ovpns
peers = [
{
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
endpoint = "185.157.162.10:9930";
# alternatively: use hostname, but that presents bootstrapping issues (e.g. if host net flakes)
# endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
allowedIPs = [ "0.0.0.0/0" ];
# nixOS says this is important for keeping NATs active
persistentKeepalive = 25;
# re-executes wg this often. docs hint that this might help wg notice DNS/hostname changes.
# so, maybe that helps if we specify endpoint as a domain name
# dynamicEndpointRefreshSeconds = 30;
# when refresh fails, try it again after this period instead.
# TODO: not avail until nixpkgs upgrade
# dynamicEndpointRefreshRestartSeconds = 5;
}
];
preSetup = "" + ''
${ip} netns add ovpns || echo "ovpns already exists"
'';
networking.iproute2.enable = true;
postShutdown = "" + ''
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
${ip} netns delete ovpns || echo "couldn't delete ovpns"
# restore rules/routes
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
${ip} rule add from all lookup local pref 0
${ip} rule del from all lookup local pref 100
'';
postSetup = "" + ''
# DOCS:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
${ip} link set ovpns-veth-a up
# mv veth-b into the ovpns namespace
${ip} link set ovpns-veth-b netns ovpns
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
${in-ns} ip link set ovpns-veth-b up
# HURRICANE ELECTRIC CONFIG:
# networking.sits = {
# hurricane = {
# remote = "216.218.226.238";
# local = "192.168.0.5";
# # local = "10.0.0.5";
# # remote = "10.0.0.1";
# # local = "10.0.0.22";
# dev = "eth0";
# ttl = 255;
# };
# };
# networking.interfaces."hurricane".ipv6 = {
# addresses = [
# # mx.uninsane.org (publically routed /64)
# {
# address = "2001:470:b:465::1";
# prefixLength = 128;
# }
# # client addr
# # {
# # address = "2001:470:a:466::2";
# # prefixLength = 64;
# # }
# ];
# routes = [
# {
# address = "::";
# prefixLength = 0;
# # via = "2001:470:a:466::1";
# }
# ];
# };
# make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination.
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
# for traffic originating at the host veth to the WAN, use the veth as our gateway
# not sure if the metric 1002 matters.
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
# give the default route lower priority
${ip} rule add from all lookup local pref 100
${ip} rule del from all lookup local pref 0
# # after configuration, we want the hurricane device to look like this:
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
# # sit txqueuelen 1000 (IPv6-in-IPv4)
# # test with:
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
# # ping 2607:f8b0:400a:80b::2004
# bridge HTTP traffic:
# any external port-80 request sent to the VPN addr will be forwarded to the rootns.
# this exists so LetsEncrypt can procure a cert for the MX over http.
# TODO: we could use _acme_challence.mx.uninsane.org CNAME to avoid this forwarding
# - <https://community.letsencrypt.org/t/where-does-letsencrypt-resolve-dns-from/37607/8>
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 80 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}:80
# 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}: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}: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.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${vpn-dns}:53
'';
};
# create a new routing table that we can use to proxy traffic out of the root namespace
# through the ovpns namespace, and to the WAN via VPN.
networking.iproute2.rttablesExtraConfig = ''
5 ovpns
'';
networking.iproute2.enable = true;
# HURRICANE ELECTRIC CONFIG:
# networking.sits = {
# hurricane = {
# remote = "216.218.226.238";
# local = "192.168.0.5";
# # local = "10.0.0.5";
# # remote = "10.0.0.1";
# # local = "10.0.0.22";
# dev = "eth0";
# ttl = 255;
# };
# };
# networking.interfaces."hurricane".ipv6 = {
# addresses = [
# # mx.uninsane.org (publically routed /64)
# {
# address = "2001:470:b:465::1";
# prefixLength = 128;
# }
# # client addr
# # {
# # address = "2001:470:a:466::2";
# # prefixLength = 64;
# # }
# ];
# routes = [
# {
# address = "::";
# prefixLength = 0;
# # via = "2001:470:a:466::1";
# }
# ];
# };
# # after configuration, we want the hurricane device to look like this:
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
# # sit txqueuelen 1000 (IPv6-in-IPv4)
# # test with:
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
# # ping 2607:f8b0:400a:80b::2004
}

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.byStore.plaintext = [
{ inherit user group; mode = "0700"; path = 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.dns.zones."uninsane.org".inet.CNAME."calibre" = "native";
}

View File

@@ -1,124 +0,0 @@
# TURN/STUN NAT traversal service
# commonly used to establish realtime calls with prosody, or possibly matrix/synapse
#
# - <https://github.com/coturn/coturn/>
# - `man turnserver`
# - config docs: <https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf>
#
# N.B. during operation it's NORMAL to see "error 401".
# during session creation:
# - client sends Allocate request
# - server replies error 401, providing a realm and nonce
# - client uses realm + nonce + shared secret to construct an auth key & call Allocate again
# - server replies Allocate Success Response
# - source: <https://stackoverflow.com/a/66643135>
#
# N.B. this safest implementation routes all traffic THROUGH A VPN
# - that adds a lot of latency, but in practice turns out to be inconsequential.
# i guess ICE allows clients to prefer the other party's lower-latency server, in practice?
# - still, this is the "safe" implementation because STUN works with IP addresses instead of domain names:
# 1. client A queries the STUN server to determine its own IP address/port.
# 2. client A tells client B which IP address/port client A is visible on.
# 3. client B contacts that IP address/port
# this only works so long as the IP address/port which STUN server sees client A on is publicly routable.
# that is NOT the case when the STUN server and client A are on the same LAN
# even if client A contacts the STUN server via its WAN address with port reflection enabled.
# hence, there's no obvious way to put the STUN server on the same LAN as either client and expect the rest to work.
{ lib, ... }:
let
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
# TURN port range (inclusive)
turnPortLow = 49152;
turnPortHigh = 49167;
turnPortRange = lib.range turnPortLow turnPortHigh;
in
{
sane.ports.ports = lib.mkMerge ([
{
"3478" = {
# this is the "control" port.
# i.e. no client data is forwarded through it, but it's where clients request tunnels.
protocol = [ "tcp" "udp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-stun-turn";
};
"5349" = {
# the other port 3478 also supports TLS/DTLS, but presumably clients wanting TLS will default 5349
protocol = [ "tcp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-stun-turn-over-tls";
};
}
] ++ (builtins.map
(port: {
"${builtins.toString port}" = let
count = port - turnPortLow + 1;
numPorts = turnPortHigh - turnPortLow + 1;
in {
protocol = [ "tcp" "udp" ];
# visibleTo.lan = true;
# visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
};
})
turnPortRange
));
services.nginx.virtualHosts."turn.uninsane.org" = {
# allow ACME to procure a cert via nginx for this domain
enableACME = true;
};
sane.dns.zones."uninsane.org".inet = {
# CNAME."turn" = "servo.wan";
# CNAME."turn" = "ovpns";
# CNAME."turn" = "native";
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
A."turn" = "%AOVPNS%";
# A."turn" = "%AWAN%";
SRV."_stun._udp" = "5 50 3478 turn";
SRV."_stun._tcp" = "5 50 3478 turn";
SRV."_stuns._tcp" = "5 50 5349 turn";
SRV."_turn._udp" = "5 50 3478 turn";
SRV."_turn._tcp" = "5 50 3478 turn";
SRV."_turns._tcp" = "5 50 5349 turn";
};
sane.derived-secrets."/var/lib/coturn/shared_secret.bin" = {
encoding = "base64";
# TODO: make this not globally readable
acl.mode = "0644";
};
sane.fs."/var/lib/coturn/shared_secret.bin".wantedBeforeBy = [ "coturn.service" ];
# provide access to certs
users.users.turnserver.extraGroups = [ "nginx" ];
services.coturn.enable = true;
services.coturn.realm = "turn.uninsane.org";
services.coturn.cert = "/var/lib/acme/turn.uninsane.org/fullchain.pem";
services.coturn.pkey = "/var/lib/acme/turn.uninsane.org/key.pem";
services.coturn.use-auth-secret = true;
services.coturn.static-auth-secret-file = "/var/lib/coturn/shared_secret.bin";
services.coturn.lt-cred-mech = true;
services.coturn.min-port = turnPortLow;
services.coturn.max-port = turnPortHigh;
# services.coturn.secure-stun = true;
services.coturn.extraConfig = lib.concatStringsSep "\n" [
"verbose"
# "Verbose" #< even MORE verbosity than "verbose"
# "no-multicast-peers" # disables sending to IPv4 broadcast addresses (e.g. 224.0.0.0/3)
"listening-ip=10.0.1.5"
# "external-ip=185.157.162.178/10.0.1.5"
"external-ip=185.157.162.178"
# "listening-ip=10.78.79.51" # can be specified multiple times; omit for *
# "external-ip=97.113.128.229/10.78.79.51"
# "external-ip=97.113.128.229"
# "mobility" # "mobility with ICE (MICE) specs support" (?)
];
}

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,31 +1,24 @@
{ ... }:
{
imports = [
./calibre.nix
./coturn.nix
./ddns-afraid.nix
./ddns-he.nix
./email
./ejabberd.nix
./freshrss.nix
./export
./gitea.nix
./goaccess.nix
./ipfs.nix
./jackett.nix
./jellyfin.nix
./kiwix-serve.nix
./komga.nix
./lemmy.nix
./matrix
./navidrome.nix
./nginx.nix
./nixserve.nix
./ntfy
./pict-rs.nix
./nginx.nix
./pleroma.nix
./postfix.nix
./postgres.nix
./prosody
./prosody.nix
./transmission.nix
./trust-dns.nix
./wikipedia.nix

View File

@@ -14,111 +14,36 @@
#
# compliance tests:
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
#
# administration:
# - `sudo -u ejabberd ejabberdctl help`
#
# federation/support matrix:
# - avatars
# - nixnet.services + dino: works in MUCs but not DMs (as of 2023 H1)
# - movim.eu + dino: works in DMs, MUCs untested (as of 2023/08/29)
# - calls
# - local + dino: audio, video, works in DMs (as of 2023/08/29)
# - movim.eu + dino: audio, video, works in DMs, no matter which side initiates (as of 2023/08/30)
# - +native-cell-number@cheogram.com + dino: audio works in DMs, no matter which side initiates (as of 2023/09/01)
# - can receive calls even if sender isn't in my roster
# - this is presumably using JMP.chat's SIP servers, which then convert it to XMPP call
#
# bugs:
# - 2023/09/01: will randomly stop federating. `systemctl restart ejabberd` fixes, but takes 10 minutes.
{ config, lib, pkgs, ... }:
let
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
# TURN port range (inclusive)
turnPortLow = 49152;
turnPortHigh = 49167;
turnPortRange = lib.range turnPortLow turnPortHigh;
in
# XXX(2023/10/15): disabled in favor of Prosody.
# everything configured below was fine: used ejabberd for several months.
lib.mkIf false
# XXX: avatar support works in MUCs but not DMs
# lib.mkIf false
{
sane.persist.sys.byStore.plaintext = [
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; }
sane.persist.sys.plaintext = [
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
];
sane.ports.ports = lib.mkMerge ([
{
"3478" = {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn";
};
"5222" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-client-to-server";
};
"5223" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpps-client-to-server"; # XMPP over TLS
};
"5269" = {
protocol = [ "tcp" ];
visibleTo.wan = true;
description = "colin-xmpp-server-to-server";
};
"5270" = {
protocol = [ "tcp" ];
visibleTo.wan = true;
description = "colin-xmpps-server-to-server"; # XMPP over TLS
};
"5280" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh";
};
"5281" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-bosh-https";
};
"5349" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-stun-turn-over-tls";
};
"5443" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
};
}
] ++ (builtins.map
(port: {
"${builtins.toString port}" = let
count = port - turnPortLow + 1;
numPorts = turnPortHigh - turnPortLow + 1;
in {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
};
})
turnPortRange
));
# this ejabberd config uses builtin STUN/TURN server, so hack to ensure no other implementation fights for ports
services.coturn.enable = false;
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 = 65535;
}];
networking.firewall.allowedUDPPortRanges = [{
from = 49152; # TURN
to = 65535;
}];
# provide access to certs
# TODO: this should just be `acme`. then we also add nginx to the `acme` group.
@@ -150,9 +75,9 @@ lib.mkIf false
useACMEHost = "uninsane.org";
};
sane.dns.zones."uninsane.org".inet = {
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";
@@ -185,285 +110,284 @@ lib.mkIf false
services.ejabberd.enable = true;
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
systemd.services.ejabberd.preStart = let
config-in = pkgs.writeText "ejabberd.yaml.in" (lib.generators.toYAML {} {
hosts = [ "uninsane.org" ];
# none | emergency | alert | critical | error | warning | notice | info | debug
loglevel = "debug";
acme.auto = false;
certfiles = [ "/var/lib/acme/uninsane.org/full.pem" ];
# ca_file = "${pkgs.cacert.unbundled}/etc/ssl/certs/";
# ca_file = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
config-in = pkgs.writeTextFile {
name = "ejabberd.yaml.in";
text = ''
hosts:
- uninsane.org
pam_userinfotype = "jid";
acl = {
admin.user = [ "colin@uninsane.org" ];
local.user_regexp = "";
loopback.ip = [ "127.0.0.0/8" "::1/128" ];
};
# none | emergency | alert | critical | error | warning | notice | info | debug
loglevel: debug
# loglevel: info
# loglevel: notice
access_rules = {
local.allow = "local";
c2s_access.allow = "all";
announce.allow = "admin";
configure.allow = "admin";
muc_create.allow = "local";
pubsub_createnode_access.allow = "all";
trusted_network.allow = "loopback";
};
acme:
auto: false
certfiles:
- /var/lib/acme/uninsane.org/full.pem
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
shaper_rules = {
# setting this to above 1 may break outgoing messages
# - maybe some servers rate limit? or just don't understand simultaneous connections?
max_s2s_connections = 1;
max_user_sessions = 10;
max_user_offline_messages = 5000;
c2s_shaper.fast = "all";
s2s_shaper.med = "all";
};
pam_userinfotype: jid
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
# this limits the bytes/sec.
# for example, burst: 3_000_000 and rate: 100_000 means:
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
shaper.fast = 1000000;
shaper.med = 500000;
# shaper.fast.rate = 1000000;
# shaper.fast.burst_size = 10000000;
# shaper.med.rate = 500000;
# shaper.med.burst_size = 5000000;
acl:
admin:
user:
- "colin@uninsane.org"
local:
user_regexp: ""
loopback:
ip:
- 127.0.0.0/8
- ::1/128
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
# s2s_use_starttls = true;
s2s_use_starttls = "optional";
# lessens 504: remote-server-timeout errors
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
negotiation_timeout = 60;
access_rules:
local:
allow: local
c2s_access:
allow: all
announce:
allow: admin
configure:
allow: admin
muc_create:
allow: local
pubsub_createnode_access:
allow: all
trusted_network:
allow: loopback
listen = [
{
port = 5222;
module = "ejabberd_c2s";
shaper = "c2s_shaper";
starttls = true;
access = "c2s_access";
}
{
port = 5223;
module = "ejabberd_c2s";
shaper = "c2s_shaper";
tls = true;
access = "c2s_access";
}
{
port = 5269;
module = "ejabberd_s2s_in";
shaper = "s2s_shaper";
}
{
port = 5270;
module = "ejabberd_s2s_in";
shaper = "s2s_shaper";
tls = true;
}
{
port = 5443;
module = "ejabberd_http";
tls = true;
request_handlers = {
"/admin" = "ejabberd_web_admin"; # TODO: ensure this actually works
"/api" = "mod_http_api"; # ejabberd API endpoint (to control server)
"/bosh" = "mod_bosh";
"/upload" = "mod_http_upload";
"/ws" = "ejabberd_http_ws";
# "/.well-known/host-meta" = "mod_host_meta";
# "/.well-known/host-meta.json" = "mod_host_meta";
};
}
{
# STUN+TURN TCP
# note that the full port range should be forwarded ("not NAT'd")
# `use_turn=true` enables both TURN *and* STUN
port = 3478;
module = "ejabberd_stun";
transport = "tcp";
use_turn = true;
turn_min_port = turnPortLow;
turn_max_port = turnPortHigh;
turn_ipv4_address = "%ANATIVE%";
}
{
# STUN+TURN UDP
port = 3478;
module = "ejabberd_stun";
transport = "udp";
use_turn = true;
turn_min_port = turnPortLow;
turn_max_port = turnPortHigh;
turn_ipv4_address = "%ANATIVE%";
}
{
# STUN+TURN TLS over TCP
port = 5349;
module = "ejabberd_stun";
transport = "tcp";
tls = true;
certfile = "/var/lib/acme/uninsane.org/full.pem";
use_turn = true;
turn_min_port = turnPortLow;
turn_max_port = turnPortHigh;
turn_ipv4_address = "%ANATIVE%";
}
];
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
shaper_rules:
# setting this to above 1 may break outgoing messages
# - maybe some servers rate limit? or just don't understand simultaneous connections?
max_s2s_connections: 1
max_user_sessions: 10
max_user_offline_messages: 5000
c2s_shaper:
fast: all
s2s_shaper:
med: all
# TODO: enable mod_fail2ban
# TODO(low): look into mod_http_fileserver for serving macros?
modules = {
# mod_adhoc = {};
# mod_announce = {
# access = "admin";
# };
# allows users to set avatars in vCard
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
mod_avatar = {};
mod_caps = {}; # for mod_pubsub
mod_carboncopy = {}; # allows multiple clients to receive a user's message
# queues messages when recipient is offline, including PEP and presence messages.
# compliance test suggests this be enabled
mod_client_state = {};
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
# this limits the bytes/sec.
# for example, burst: 3_000_000 and rate: 100_000 means:
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
shaper:
fast: 1000000
med: 500000
# fast:
# - rate: 1000000
# - burst_size: 10000000
# med:
# - rate: 500000
# - burst_size: 5000000
# mod_conversejs: TODO: enable once on 21.12
# allows clients like Dino to discover where to upload files
mod_disco.server_info = [
{
modules = "all";
name = "abuse-addresses";
urls = [
"mailto:admin.xmpp@uninsane.org"
"xmpp:colin@uninsane.org"
];
}
{
modules = "all";
name = "admin-addresses";
urls = [
"mailto:admin.xmpp@uninsane.org"
"xmpp:colin@uninsane.org"
];
}
];
mod_http_upload = {
host = "upload.xmpp.uninsane.org";
hosts = [ "upload.xmpp.uninsane.org" ];
put_url = "https://@HOST@:5443/upload";
dir_mode = "0750";
file_mode = "0750";
rm_on_unregister = false;
};
# allow discoverability of BOSH and websocket endpoints
# TODO: enable once on ejabberd 22.05 (presently 21.04)
# mod_host_meta = {};
mod_jidprep = {}; # probably not needed: lets clients normalize jids
mod_last = {}; # allow other users to know when i was last online
mod_mam = {
# Mnesia is limited to 2GB, better to use an SQL backend
# For small servers SQLite is a good fit and is very easy
# to configure. Uncomment this when you have SQL configured:
# db_type: sql
assume_mam_usage = true;
default = "always";
};
mod_muc = {
access = [ "allow" ];
access_admin = { allow = "admin"; };
access_create = "muc_create";
access_persistent = "muc_create";
access_mam = [ "allow" ];
history_size = 100; # messages to show new participants
host = "muc.xmpp.uninsane.org";
hosts = [ "muc.xmpp.uninsane.org" ];
default_room_options = {
anonymous = false;
lang = "en";
persistent = true;
mam = true;
};
};
mod_muc_admin = {};
mod_offline = {
# store messages for a user when they're offline (TODO: understand multi-client workflow?)
access_max_user_messages = "max_user_offline_messages";
store_groupchat = true;
};
mod_ping = {};
mod_privacy = {}; # deprecated, but required for `ejabberctl export_piefxis`
mod_private = {}; # allow local clients to persist arbitrary data on my server
# push notifications to services integrated with e.g. Apple/Android.
# default is for a maximum amount of PII to be withheld, since these push notifs
# generally traverse 3rd party services. can opt to include message body, etc, though.
mod_push = {};
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
mod_push_keepalive = {};
mod_roster = {
versioning = true;
};
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
# s2s dialback to verify inbound messages
# unclear to what degree the XMPP network requires this
mod_s2s_dialback = {};
mod_shared_roster = {}; # creates groups for @all, @online, and anything manually administered?
mod_stream_mgmt = {
# resend undelivered messages if the origin client is offline
resend_on_timeout = "if_offline";
};
# fallback for when DNS-based STUN discovery is unsupported.
# - see: <https://xmpp.org/extensions/xep-0215.html>
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
mod_stun_disco = {};
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
mod_vcard = {
allow_return_all = true; # all users are discoverable (?)
host = "vjid.xmpp.uninsane.org";
hosts = [ "vjid.xmpp.uninsane.org" ];
search = true;
};
mod_vcard_xupdate = {}; # needed for avatars
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
mod_pubsub = {
#^ needed for avatars
access_createnode = "pubsub_createnode_access";
host = "pubsub.xmpp.uninsane.org";
hosts = [ "pubsub.xmpp.uninsane.org" ];
ignore_pep_from_offline = false;
last_item_cache = true;
plugins = [
"pep"
"flat"
];
force_node_config = {
# ensure client bookmarks are private
"storage:bookmarks:" = {
"access_model" = "whitelist";
};
"urn:xmpp:avatar:data" = {
"access_model" = "open";
};
"urn:xmpp:avatar:metadata" = {
"access_model" = "open";
};
};
};
mod_version = {};
};
});
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
# s2s_use_starttls: true
s2s_use_starttls: optional
# lessens 504: remote-server-timeout errors
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
negotiation_timeout: 60
listen:
-
port: 5222
module: ejabberd_c2s
shaper: c2s_shaper
starttls: true
access: c2s_access
-
port: 5223
module: ejabberd_c2s
shaper: c2s_shaper
tls: true
access: c2s_access
-
port: 5269
module: ejabberd_s2s_in
shaper: s2s_shaper
-
port: 5270
module: ejabberd_s2s_in
shaper: s2s_shaper
tls: true
-
port: 5443
module: ejabberd_http
tls: true
request_handlers:
/admin: ejabberd_web_admin # TODO: ensure this actually works
/api: mod_http_api # ejabberd API endpoint (to control server)
/bosh: mod_bosh
/upload: mod_http_upload
/ws: ejabberd_http_ws
# /.well-known/host-meta: mod_host_meta
# /.well-known/host-meta.json: mod_host_meta
-
# STUN+TURN TCP
# note that the full port range should be forwarded ("not NAT'd")
# `use_turn=true` enables both TURN *and* STUN
port: 3478
module: ejabberd_stun
transport: tcp
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
-
# STUN+TURN UDP
port: 3478
module: ejabberd_stun
transport: udp
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
-
# STUN+TURN TLS over TCP
port: 5349
module: ejabberd_stun
transport: tcp
tls: true
certfile: /var/lib/acme/uninsane.org/full.pem
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
# TODO: enable mod_fail2ban
# TODO(low): look into mod_http_fileserver for serving macros?
modules:
# mod_adhoc: {}
# mod_announce:
# access: admin
# allows users to set avatars in vCard
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
mod_avatar: {}
mod_caps: {} # for mod_pubsub
mod_carboncopy: {} # allows multiple clients to receive a user's message
# queues messages when recipient is offline, including PEP and presence messages.
# compliance test suggests this be enabled
mod_client_state: {}
# mod_conversejs: TODO: enable once on 21.12
# allows clients like Dino to discover where to upload files
mod_disco:
server_info:
-
modules: all
name: abuse-addresses
urls:
- "mailto:admin.xmpp@uninsane.org"
- "xmpp:colin@uninsane.org"
-
modules: all
name: admin-addresses
urls:
- "mailto:admin.xmpp@uninsane.org"
- "xmpp:colin@uninsane.org"
mod_http_upload:
host: upload.xmpp.uninsane.org
hosts:
- upload.xmpp.uninsane.org
put_url: "https://@HOST@:5443/upload"
dir_mode: "0750"
file_mode: "0750"
rm_on_unregister: false
# allow discoverability of BOSH and websocket endpoints
# TODO: enable once on ejabberd 22.05 (presently 21.04)
# mod_host_meta: {}
mod_jidprep: {} # probably not needed: lets clients normalize jids
mod_last: {} # allow other users to know when i was last online
mod_mam:
# Mnesia is limited to 2GB, better to use an SQL backend
# For small servers SQLite is a good fit and is very easy
# to configure. Uncomment this when you have SQL configured:
# db_type: sql
assume_mam_usage: true
default: always
mod_muc:
access:
- allow
access_admin:
- allow: admin
access_create: muc_create
access_persistent: muc_create
access_mam:
- allow
history_size: 100 # messages to show new participants
host: muc.xmpp.uninsane.org
hosts:
- muc.xmpp.uninsane.org
default_room_options:
anonymous: false
lang: en
persistent: true
mam: true
mod_muc_admin: {}
mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?)
access_max_user_messages: max_user_offline_messages
store_groupchat: true
mod_ping: {}
mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis`
mod_private: {} # allow local clients to persist arbitrary data on my server
# push notifications to services integrated with e.g. Apple/Android.
# default is for a maximum amount of PII to be withheld, since these push notifs
# generally traverse 3rd party services. can opt to include message body, etc, though.
mod_push: {}
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
mod_push_keepalive: {}
mod_roster:
versioning: true
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
# s2s dialback to verify inbound messages
# unclear to what degree the XMPP network requires this
mod_s2s_dialback: {}
mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered?
mod_stream_mgmt:
resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline
# fallback for when DNS-based STUN discovery is unsupported.
# - see: <https://xmpp.org/extensions/xep-0215.html>
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
mod_stun_disco: {}
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
mod_vcard:
allow_return_all: true # all users are discoverable (?)
host: vjid.xmpp.uninsane.org
hosts:
- vjid.xmpp.uninsane.org
search: true
mod_vcard_xupdate: {} # needed for avatars
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
mod_pubsub: # needed for avatars
access_createnode: pubsub_createnode_access
host: pubsub.xmpp.uninsane.org
hosts:
- pubsub.xmpp.uninsane.org
ignore_pep_from_offline: false
last_item_cache: true
plugins:
- pep
- flat
force_node_config:
# ensure client bookmarks are private
storage:bookmarks:
access_model: whitelist
urn:xmpp:avatar:data:
access_model: open
urn:xmpp:avatar:metadata:
access_model: open
mod_version: {}
'';
};
sed = "${pkgs.gnused}/bin/sed";
in ''
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
# 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/g" ${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,44 +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
#
# debugging: general connectivity issues
# - test that inbound port 25 is unblocked:
# - `curl https://canyouseeme.org/ --data 'port=25&IP=185.157.162.178' | grep 'see your service'`
# - and retry with port 465, 587
# - i think this API requires the queried IP match the source IP
# - if necessary, `systemctl stop postfix` and `sudo nc -l 185.157.162.178 25`, then try https://canyouseeme.org
{ ... }:
{
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.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,53 +0,0 @@
{ config, ... }:
{
imports = [
./nfs.nix
./sftpgo.nix
];
users.groups.export = {};
fileSystems."/var/export/media" = {
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
device = "/var/lib/uninsane/media";
options = [ "rbind" ];
};
# fileSystems."/var/export/playground" = {
# device = config.fileSystems."/mnt/persist/ext".device;
# fsType = "btrfs";
# options = [
# "subvol=export-playground"
# "compress=zstd"
# "defaults"
# ];
# };
# N.B.: the backing directory should be manually created here **as a btrfs subvolume** and with a quota.
# - `sudo btrfs subvolume create /mnt/persist/ext/persist/var/export/playground`
# - `sudo btrfs quota enable /mnt/persist/ext/persist/var/export/playground`
# - `sudo btrfs quota rescan -sw /mnt/persist/ext/persist/var/export/playground`
# to adjust the limits (which apply at the block layer, i.e. post-compression):
# - `sudo btrfs qgroup limit 20G /mnt/persist/ext/persist/var/export/playground`
# to query the quota/status:
# - `sudo btrfs qgroup show -re /var/export/playground`
sane.persist.sys.ext = [
{ user = "root"; group = "export"; mode = "0775"; path = "/var/export/playground"; }
];
sane.fs."/var/export/README.md" = {
wantedBy = [ "nfs.service" "sftpgo.service" ];
file.text = ''
- media/ read-only: Videos, Music, Books, etc
- playground/ read-write: use it to share files with other users of this server
'';
};
sane.fs."/var/export/playground/README.md" = {
wantedBy = [ "nfs.service" "sftpgo.service" ];
file.text = ''
this directory is intentionally read+write by anyone with access (i.e. on the LAN).
- share files
- write poetry
- be a friendly troll
'';
};
}

View File

@@ -1,110 +0,0 @@
# docs:
# - <https://nixos.wiki/wiki/NFS>
# - <https://wiki.gentoo.org/wiki/Nfs-utils>
# system files:
# - /etc/exports
# system services:
# - nfs-server.service
# - nfs-idmapd.service
# - nfs-mountd.service
# - nfsdcld.service
# - rpc-statd.service
# - rpcbind.service
#
# TODO: force files to be 755, or 750.
# - could maybe be done with some mount option?
{ config, lib, ... }:
{
services.nfs.server.enable = true;
# see which ports NFS uses with:
# - `rpcinfo -p`
sane.ports.ports."111" = {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
description = "NFS server portmapper";
};
sane.ports.ports."2049" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "NFS server";
};
sane.ports.ports."4000" = {
protocol = [ "udp" ];
visibleTo.lan = true;
description = "NFS server status daemon";
};
sane.ports.ports."4001" = {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
description = "NFS server lock daemon";
};
sane.ports.ports."4002" = {
protocol = [ "tcp" "udp" ];
visibleTo.lan = true;
description = "NFS server mount daemon";
};
# NFS4 allows these to float, but NFS3 mandates specific ports, so fix them for backwards compat.
services.nfs.server.lockdPort = 4001;
services.nfs.server.mountdPort = 4002;
services.nfs.server.statdPort = 4000;
# format:
# fspoint visibility(options)
# options:
# - see: <https://wiki.gentoo.org/wiki/Nfs-utils#Exports>
# - see [man 5 exports](https://linux.die.net/man/5/exports)
# - insecure: require clients use src port > 1024
# - rw, ro (default)
# - async, sync (default)
# - no_subtree_check (default), subtree_check: verify not just that files requested by the client live
# in the expected fs, but also that they live under whatever subdirectory of that fs is exported.
# - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`.
# - crossmnt: reveal filesystems that are mounted under this endpoint
# - fsid: must be zero for the root export
# - fsid=root is alias for fsid=0
# - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts.
# - all_squash: rewrite all client requests such that they come from anonuid/anongid
# - any files a user creates are owned by local anonuid/anongid.
# - users can read any local file which anonuid/anongid would be able to read.
# - users can't chown to/away from anonuid/anongid.
# - users can chmod files they own, to anything (making them unreadable to non-`nfsuser` export users, like FTP).
# - `stat` remains unchanged, returning the real UIDs/GIDs to the client.
# - thus programs which check `uid` or `gid` before trying an operation may incorrectly conclude they can't perform some op.
#
# 10.0.0.0/8 to export both to LAN (readonly, unencrypted) and wg vpn (read-write, encrypted)
services.nfs.server.exports =
let
fmtExport = { export, baseOpts, extraLanOpts ? [], extraVpnOpts ? [] }:
let
always = [ "subtree_check" ];
lanOpts = always ++ baseOpts ++ extraLanOpts;
vpnOpts = always ++ baseOpts ++ extraVpnOpts;
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
in lib.concatStringsSep "\n" [
(fmtExport {
export = "/var/export";
baseOpts = [ "crossmnt" "fsid=root" ];
extraLanOpts = [ "ro" ];
extraVpnOpts = [ "rw" "no_root_squash" ];
})
(fmtExport {
export = "/var/export/playground";
baseOpts = [
"mountpoint"
"all_squash"
"rw"
"anonuid=${builtins.toString config.users.users.nfsuser.uid}"
"anongid=${builtins.toString config.users.groups.export.gid}"
];
})
];
users.users.nfsuser = {
description = "virtual user for anonymous NFS operations";
group = "export";
isSystemUser = true;
};
}

View File

@@ -1,184 +0,0 @@
# docs:
# - <https://github.com/drakkan/sftpgo>
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
#
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
#
# TODO: change umask so sftpgo-created files default to 644.
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
{ config, lib, pkgs, sane-lib, ... }:
let
# user permissions:
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
# - "*" = grant all permissions
# - read-only perms:
# - "list" = list files and directories
# - "download"
# - rw perms:
# - "upload"
# - "overwrite" = allow uploads to replace existing files
# - "delete" = delete files and directories
# - "delete_files"
# - "delete_dirs"
# - "rename" = rename files and directories
# - "rename_files"
# - "rename_dirs"
# - "create_dirs"
# - "create_symlinks"
# - "chmod"
# - "chown"
# - "chtimes" = change atime/mtime (access and modification times)
#
# home_dir:
# - it seems (empirically) that a user can't cd above their home directory.
# though i don't have a reference for that in the docs.
authResponseSuccess = {
status = 1;
username = "anonymous";
expiration_date = 0;
home_dir = "/var/export";
# uid/gid 0 means to inherit sftpgo uid.
# - i.e. users can't read files which Linux user `sftpgo` can't read
# - uploaded files belong to Linux user `sftpgo`
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
uid = 0;
gid = 0;
# uid = 65534;
# gid = 65534;
max_sessions = 0;
# quota_*: 0 means to not use SFTP's quota system
quota_size = 0;
quota_files = 0;
permissions = {
"/" = [ "list" "download" ];
"/playground" = [
# read-only:
"list"
"download"
# write:
"upload"
"overwrite"
"delete"
"rename"
"create_dirs"
"create_symlinks"
# intentionally omitted:
# "chmod"
# "chown"
# "chtimes"
];
};
upload_bandwidth = 0;
download_bandwidth = 0;
filters = {
allowed_ip = [];
denied_ip = [];
};
public_keys = [];
# other fields:
# ? groups
# ? virtual_folders
};
authResponseFail = {
username = "";
};
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
pname = "sftpgo_external_auth_hook";
src = ./.;
pkgs = [ "coreutils" ];
};
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
'';
in
{
# Client initiates a FTP "control connection" on port 21.
# - this handles the client -> server commands, and the server -> client status, but not the actual data
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
# - 50000-50100 is a common port range for this.
sane.ports.ports = {
"21" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-FTP server";
};
} // (sane-lib.mapToAttrs
(port: {
name = builtins.toString port;
value = {
protocol = [ "tcp" ];
visibleTo.lan = true;
description = "colin-FTP server data port range";
};
})
(lib.range 50000 50100)
);
services.sftpgo = {
enable = true;
group = "export";
settings = {
ftpd = {
bindings = [
{
# binding this means any wireguard client can connect
address = "10.0.10.5";
port = 21;
debug = true;
}
{
# binding this means any LAN client can connect
address = "10.78.79.51";
port = 21;
debug = true;
}
];
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
disable_active_mode = true;
hash_support = true;
passive_port_range = {
start = 50000;
end = 50100;
};
banner = ''
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
Username: "anonymous"
Password: "anonymous"
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
'';
};
data_provider = {
driver = "memory";
external_auth_hook = "${authProgram}";
# track_quota:
# - 0: disable quota tracking
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
# track_quota = 2;
};
};
};
users.users.sftpgo.extraGroups = [ "export" ];
systemd.services.sftpgo.serviceConfig = {
ReadOnlyPaths = [ "/var/export" ];
ReadWritePaths = [ "/var/export/playground" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
Restart = "always";
RestartSec = "20s";
};
}

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p coreutils
# vim: set filetype=bash :
#
# available environment variables:
# - SFTPGO_AUTHD_USERNAME
# - SFTPGO_AUTHD_USER
# - SFTPGO_AUTHD_IP
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
# - SFTPGO_AUTHD_PASSWORD
# - SFTPGO_AUTHD_PUBLIC_KEY
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
# - SFTPGO_AUTHD_TLS_CERT
#
#
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
cat "$2"
else
cat "$1"
fi

View File

@@ -15,8 +15,8 @@
owner = config.users.users.freshrss.name;
mode = "0400";
};
sane.persist.sys.byStore.plaintext = [
{ user = "freshrss"; group = "freshrss"; path = "/var/lib/freshrss"; }
sane.persist.sys.plaintext = [
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
];
services.freshrss.enable = true;
@@ -59,5 +59,5 @@
# the routing is handled by services.freshrss.virtualHost
};
sane.dns.zones."uninsane.org".inet.CNAME."rss" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."rss" = "native";
}

View File

@@ -1,16 +1,18 @@
# config options: <https://docs.gitea.io/en-us/administration/config-cheat-sheet/>
{ config, pkgs, lib, ... }:
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode? could be more granular
{ user = "git"; group = "gitea"; path = "/var/lib/gitea"; }
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
];
services.gitea.enable = true;
services.gitea.user = "git"; # default is 'gitea'
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.
@@ -98,12 +95,5 @@
};
};
sane.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";
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
}

View File

@@ -64,5 +64,5 @@
};
};
sane.dns.zones."uninsane.org".inet.CNAME."sink" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."sink" = "native";
}

View File

@@ -10,9 +10,9 @@
lib.mkIf false # i don't actively use ipfs anymore
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode? could be more granular
{ user = "261"; group = "261"; path = "/var/lib/ipfs"; }
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
];
networking.firewall.allowedTCPPorts = [ 4001 ];
@@ -34,7 +34,7 @@ lib.mkIf false # i don't actively use ipfs anymore
};
};
sane.dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
# services.ipfs.enable = true;
services.kubo.localDiscovery = true;

View File

@@ -1,9 +1,9 @@
{ ... }:
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
{ user = "root"; group = "root"; path = "/var/lib/jackett"; }
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
];
services.jackett.enable = true;
@@ -24,10 +24,9 @@
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
recommendedProxySettings = true;
};
};
sane.dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
}

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.byStore.plaintext = [
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; path = "/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
@@ -121,7 +61,7 @@
};
};
sane.dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
services.jellyfin.enable = true;
}

View File

@@ -13,5 +13,5 @@
locations."/".proxyPass = "http://127.0.0.1:8013";
};
sane.dns.zones."uninsane.org".inet.CNAME."w" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."w" = "native";
}

View File

@@ -1,22 +0,0 @@
{ config, ... }:
let
svc-cfg = config.services.komga;
inherit (svc-cfg) user group port stateDir;
in
{
sane.persist.sys.byStore.plaintext = [
{ inherit user group; mode = "0700"; path = 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.dns.zones."uninsane.org".inet.CNAME."komga" = "native";
}

View File

@@ -1,85 +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, pkgs, ... }:
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?
pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
# as of v 0.4.2, all non-GIF video is forcibly transcoded.
# that breaks lemmy, because of the request latency.
# and it eats up hella CPU.
# pict-rs is iffy around video altogether: mp4 seems the best supported.
postPatch = (upstream.postPatch or "") + ''
substituteInPlace src/validate.rs \
--replace 'if transcode_options.needs_reencode() {' 'if false {'
'';
});
in {
services.lemmy = {
enable = true;
settings.hostname = "lemmy.uninsane.org";
# federation.debug forces outbound federation queries to be run synchronously
# N.B.: this option might not be read for 0.17.0+? <https://github.com/LemmyNet/lemmy/blob/c32585b03429f0f76d1e4ff738786321a0a9df98/RELEASES.md#upgrade-instructions>
# 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";
# RUST_LOG = "trace";
# 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.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
#v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this.
services.pict-rs.package = pict-rs;
# pict-rs configuration is applied in this order:
# - via toml
# - via env vars (overrides everything above)
# - via CLI flags (overrides everything above)
# some of the CLI flags have defaults, making it the only actual way to configure certain things even when docs claim otherwise.
# CLI args: <https://git.asonix.dog/asonix/pict-rs#user-content-running>
systemd.services.pict-rs.serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
"${lib.getBin pict-rs}/bin/pict-rs run"
"--media-max-frame-count" (builtins.toString (30*60*60))
"--media-process-timeout 120"
"--media-enable-full-video true" # allow audio
]);
}

View File

@@ -1,92 +1,75 @@
# 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>
#
# ENABLING PUSH NOTIFICATIONS (with UnifiedPush/ntfy):
# - Matrix "pushers" API spec: <https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3pushersset>
# - first, view notification settings:
# - obtain your client's auth token. e.g. Element -> profile -> help/about -> access token.
# - `curl --header 'Authorization: Bearer <your_access_token>' localhost:8008/_matrix/client/v3/pushers | jq .`
# - enable a new notification destination:
# - `curl --header "Authorization: Bearer <your_access_token>" --data '{ "app_display_name": "<topic>", "app_id": "ntfy.uninsane.org", "data": { "url": "https://ntfy.uninsane.org/_matrix/push/v1/notify", "format": "event_id_only" }, "device_display_name": "<topic>", "kind": "http", "lang": "en-US", "profile_tag": "", "pushkey": "<topic>" }' localhost:8008/_matrix/client/v3/pushers/set`
# - delete a notification destination by setting `kind` to `null` (otherwise, request is identical to above)
#
# 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
];
sane.persist.sys.byStore.plaintext = [
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/matrix-synapse"; }
# 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"; }
];
services.matrix-synapse.enable = true;
services.matrix-synapse.settings = {
# this changes the default log level from INFO to WARN.
# maybe there's an easier way?
log_config = ./synapse-log_level.yaml;
server_name = "uninsane.org";
# this changes the default log level from INFO to WARN.
# maybe there's an easier way?
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
services.matrix-synapse.settings.server_name = "uninsane.org";
# services.matrix-synapse.enable_registration_captcha = true;
# services.matrix-synapse.enable_registration_without_verification = true;
enable_registration = true;
# services.matrix-synapse.registration_shared_secret = "<shared key goes here>";
# services.matrix-synapse.enable_registration_captcha = true;
# services.matrix-synapse.enable_registration_without_verification = true;
services.matrix-synapse.settings.enable_registration = true;
# services.matrix-synapse.registration_shared_secret = "<shared key goes here>";
# default for listeners is port = 8448, tls = true, x_forwarded = false.
# we change this because the server is situated behind nginx.
listeners = [
{
port = 8008;
bind_addresses = [ "127.0.0.1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [
{
names = [ "client" "federation" ];
compress = false;
}
];
}
];
ip_range_whitelist = [
# to communicate with ntfy.uninsane.org push notifs.
# TODO: move this to some non-shared loopback device: we don't want Matrix spouting http requests to *anything* on this machine
"10.78.79.51"
];
x_forwarded = true; # because we proxy matrix behind nginx
max_upload_size = "100M"; # default is "50M"
admin_contact = "admin.matrix@uninsane.org";
registrations_require_3pid = [ "email" ];
};
services.matrix-synapse.extraConfigFiles = [
config.sops.secrets."matrix_synapse_secrets.yaml".path
# default for listeners is port = 8448, tls = true, x_forwarded = false.
# we change this because the server is situated behind nginx.
services.matrix-synapse.settings.listeners = [
{
port = 8008;
bind_addresses = [ "127.0.0.1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [
{
names = [ "client" "federation" ];
compress = false;
}
];
}
];
systemd.services.matrix-synapse.postStart = ''
ACCESS_TOKEN=$(${pkgs.coreutils}/bin/cat ${config.sops.secrets.matrix_access_token.path})
TOPIC=$(${pkgs.coreutils}/bin/cat ${config.sops.secrets.ntfy-sh-topic.path})
services.matrix-synapse.settings.admin_contact = "admin.matrix@uninsane.org";
services.matrix-synapse.settings.registrations_require_3pid = [ "email" ];
echo "ensuring ntfy push gateway"
${pkgs.curl}/bin/curl \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--data "{ \"app_display_name\": \"ntfy-adapter\", \"app_id\": \"ntfy.uninsane.org\", \"data\": { \"url\": \"https://ntfy.uninsane.org/_matrix/push/v1/notify\", \"format\": \"event_id_only\" }, \"device_display_name\": \"ntfy-adapter\", \"kind\": \"http\", \"lang\": \"en-US\", \"profile_tag\": \"\", \"pushkey\": \"$TOPIC\" }" \
localhost:8008/_matrix/client/v3/pushers/set
echo "registered push gateways:"
${pkgs.curl}/bin/curl \
--header "Authorization: Bearer $ACCESS_TOKEN" \
localhost:8008/_matrix/client/v3/pushers \
| ${pkgs.jq}/bin/jq .
'';
services.matrix-synapse.extraConfigFiles = [
config.sops.secrets.matrix_synapse_secrets.path
];
# services.matrix-synapse.extraConfigFiles = [builtins.toFile "matrix-synapse-extra-config" ''
# admin_contact: "admin.matrix@uninsane.org"
# registrations_require_3pid:
# - email
# email:
# smtp_host: "mx.uninsane.org"
# smtp_port: 587
# smtp_user: "matrix-synapse"
# smtp_pass: "${secrets.matrix-synapse.smtp_pass}"
# require_transport_security: true
# enable_tls: true
# notif_from: "%(app)s <notify.matrix@uninsane.org>"
# app_name: "Uninsane Matrix"
# enable_notifs: true
# validation_token_lifetime: 96h
# invite_client_location: "https://web.matrix.uninsane.org"
# subjects:
# email_validation: "[%(server_name)s] Validate your email"
# ''];
# new users may be registered on the CLI:
# register_new_matrix_user -c /nix/store/8n6kcka37jhmi4qpd2r03aj71pkyh21s-homeserver.yaml http://localhost:8008
@@ -114,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.
@@ -148,18 +127,13 @@
};
};
sane.dns.zones."uninsane.org".inet = {
sane.services.trust-dns.zones."uninsane.org".inet = {
CNAME."matrix" = "native";
CNAME."web.matrix" = "native";
};
sops.secrets."matrix_synapse_secrets.yaml" = {
sops.secrets."matrix_synapse_secrets" = {
owner = config.users.users.matrix-synapse.name;
};
sops.secrets."matrix_access_token" = {
owner = config.users.users.matrix-synapse.name;
};
# provide access to ntfy-sh-topic secret
users.users.matrix-synapse.extraGroups = [ "ntfy-sh" ];
}

View File

@@ -1,12 +1,7 @@
{ 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.byStore.plaintext = [
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/mx-puppet-discord"; }
sane.persist.sys.plaintext = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
];
services.matrix-synapse.settings.app_service_config_files = [

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,123 +1,21 @@
# config docs:
# - <https://github.com/matrix-org/matrix-appservice-irc/blob/develop/config.sample.yaml>
# probably want to remove that.
{ config, lib, ... }:
let
ircServer = { name, additionalAddresses ? [], sasl ? true, port ? 6697 }: 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;
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.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode?
{ user = "matrix-appservice-irc"; group = "matrix-appservice-irc"; path = "/var/lib/matrix-appservice-irc"; }
# user and group are both "matrix-appservice-irc"
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }
];
# XXX: matrix-appservice-irc PreStart tries to chgrp the registration.yml to matrix-synapse,
# which requires matrix-appservice-irc to be of that group
users.users.matrix-appservice-irc.extraGroups = [ "matrix-synapse" ];
# weird race conditions around registration.yml mean we want matrix-synapse to be of matrix-appservice-irc group too.
users.users.matrix-synapse.extraGroups = [ "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";
@@ -130,39 +28,70 @@ 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
# - #mnt-reform
};
"irc.myanonamouse.net" = ircServer {
name = "MyAnonamouse";
additionalAddresses = [ "irc2.myanonamouse.net" ];
sasl = false;
};
"irc.oftc.net" = ircServer {
name = "oftc";
sasl = false;
# notable channels:
# - #sxmo
# - #sxmo-offtopic
};
"irc.rizon.net" = ircServer { name = "Rizon"; };
};
};
};
systemd.services.matrix-appservice-irc.serviceConfig = {
# XXX 2023/06/20: nixos specifies this + @aio and @memlock as forbidden
# the service actively uses at least one of these, and both of them are fairly innocuous
SystemCallFilter = lib.mkForce "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @setuid @swap";
};
}

View File

@@ -2,14 +2,11 @@
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
{ config, pkgs, ... }:
{
sane.persist.sys.byStore.plaintext = [
{ user = "mautrix-signal"; group = "mautrix-signal"; path = "/var/lib/mautrix-signal"; }
{ user = "signald"; group = "signald"; path = "/var/lib/signald"; }
sane.persist.sys.plaintext = [
{ user = "mautrix-signal"; group = "mautrix-signal"; directory = "/var/lib/mautrix-signal"; }
{ 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

@@ -1,8 +1,8 @@
{ lib, ... }:
{
sane.persist.sys.byStore.plaintext = [
{ user = "navidrome"; group = "navidrome"; path = "/var/lib/navidrome"; }
sane.persist.sys.plaintext = [
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/navidrome"; }
];
services.navidrome.enable = true;
services.navidrome.settings = {
@@ -36,5 +36,5 @@
locations."/".proxyPass = "http://127.0.0.1:4533";
};
sane.dns.zones."uninsane.org".inet.CNAME."music" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."music" = "native";
}

View File

@@ -1,5 +1,4 @@
# docs: <https://nixos.wiki/wiki/Nginx>
# docs: <https://nginx.org/en/docs/>
# docs: https://nixos.wiki/wiki/Nginx
{ config, lib, pkgs, ... }:
let
@@ -14,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 = ''
@@ -102,8 +89,7 @@ in
};
# allow ActivityPub clients to discover how to reach @user@uninsane.org
# see: https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
# not sure this makes sense while i run multiple AP services (pleroma, lemmy)
# TODO: waiting on https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
# locations."/.well-known/nodeinfo" = {
# proxyPass = "http://127.0.0.1:4000";
# extraConfig = pleromaExtraConfig;
@@ -134,10 +120,10 @@ in
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin.acme@uninsane.org";
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode?
{ user = "acme"; group = "acme"; path = "/var/lib/acme"; }
{ user = "colin"; group = "users"; path = "/var/www/sites"; }
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
];
# let's encrypt default chain looks like:

View File

@@ -14,8 +14,8 @@
'';
};
sane.dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
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,14 +0,0 @@
# ntfy: UnifiedPush notification delivery system
# - used to get push notifications out of Matrix and onto a Phone (iOS, Android, or a custom client)
{ config, ... }:
{
imports = [
./ntfy-waiter.nix
./ntfy-sh.nix
];
sops.secrets."ntfy-sh-topic" = {
mode = "0440";
owner = config.users.users.ntfy-sh.name;
group = config.users.users.ntfy-sh.name;
};
}

View File

@@ -1,92 +0,0 @@
# ntfy: UnifiedPush notification delivery system
# - used to get push notifications out of Matrix and onto a Phone (iOS, Android, or a custom client)
#
# config options:
# - <https://docs.ntfy.sh/config/#config-options>
#
# usage:
# - ntfy sub https://ntfy.uninsane.org/TOPIC
# - ntfy pub https://ntfy.uninsane.org/TOPIC "my message"
# in production, TOPIC is a shared secret between the publisher (Matrix homeserver) and the subscriber (phone)
#
# administering:
# - sudo -u ntfy-sh ntfy access
#
# debugging:
# - make sure that the keepalives are good:
# - on the subscriber machine, run `lsof -i4` to find the port being used
# - `sudo tcpdump tcp port <p>`
# - shouldn't be too spammy
#
# matrix integration:
# - the user must manually point synapse to the ntfy endpoint:
# - `curl --header "Authorization: <your_token>" --data '{ "app_display_name": "sane-nix moby", "app_id": "ntfy.uninsane.org", "data": { "url": "https://ntfy.uninsane.org/_matrix/push/v1/notify", "format": "event_id_only" }, "device_display_name": "sane-nix moby", "kind": "http", "lang": "en-US", "profile_tag": "", "pushkey": "https://ntfy.uninsane.org/TOPIC" }' localhost:8008/_matrix/client/v3/pushers/set`
# where the token is grabbed from Element's help&about page when logged in
# - to remove, send this `curl` with `"kind": null`
{ config, lib, pkgs, ... }:
let
# subscribers need a non-443 public port to listen on as a way to easily differentiate this traffic
# at the IP layer, to enable e.g. wake-on-lan.
altPort = 2587;
in
{
sane.persist.sys.byStore.plaintext = [
# not 100% necessary to persist this, but ntfy does keep a 12hr (by default) cache
# for pushing notifications to users who become offline.
# ACLs also live here.
{ user = "ntfy-sh"; group ="ntfy-sh"; path = "/var/lib/ntfy-sh"; }
];
services.ntfy-sh.enable = true;
services.ntfy-sh.settings = {
base-url = "https://ntfy.uninsane.org";
behind-proxy = true; # not sure if needed
# keepalive interval is a ntfy-specific keepalive thing, where it sends actual data down the wire.
# it's not simple TCP keepalive.
# defaults to 45s.
# note that the client may still do its own TCP-level keepalives, typically every 30s
keepalive-interval = "15m";
log-level = "trace"; # trace, debug, info (default), warn, error
auth-default-access = "deny-all";
};
systemd.services.ntfy-sh.serviceConfig.DynamicUser = lib.mkForce false;
systemd.services.ntfy-sh.preStart = ''
# make this specific topic read-write by world
# it would be better to use the token system, but that's extra complexity for e.g.
# how do i plumb a secret into the Matrix notification pusher
#
# note that this will fail upon first run, i.e. before ntfy has created its db.
# just restart the service.
topic=$(cat ${config.sops.secrets.ntfy-sh-topic.path})
${pkgs.ntfy-sh}/bin/ntfy access everyone "$topic" read-write
'';
services.nginx.virtualHosts."ntfy.uninsane.org" = {
forceSSL = true;
enableACME = true;
listen = [
{ addr = "0.0.0.0"; port = altPort; ssl = true; }
{ addr = "0.0.0.0"; port = 443; ssl = true; }
{ addr = "0.0.0.0"; port = 80; ssl = false; }
];
locations."/" = {
proxyPass = "http://127.0.0.1:2586";
proxyWebsockets = true; #< support websocket upgrades. without that, `ntfy sub` hangs silently
recommendedProxySettings = true; #< adds headers so ntfy logs include the real IP
extraConfig = ''
# absurdly long timeout (86400s=24h) so that we never hang up on clients.
# make sure the client is smart enough to detect a broken proxy though!
proxy_read_timeout 86400s;
'';
};
};
sane.dns.zones."uninsane.org".inet.CNAME."ntfy" = "native";
sane.ports.ports."${builtins.toString altPort}" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-ntfy.uninsane.org";
};
}

View File

@@ -1,151 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p ntfy-sh
import argparse
import logging
import os
import socket
import subprocess
import sys
import threading
import time
logger = logging.getLogger(__name__)
LISTEN_QUEUE = 3
WAKE_MESSAGE = b'notification\n'
class Client:
def __init__(self, sock, addr_info, live_after: float):
self.live_after = live_after
self.sock = sock
self.addr_info = addr_info
def __cmp__(self, other: 'Client'):
return cmp(self.addr_info, other.addr_info)
def try_notify(self, message: bytes) -> bool:
"""
returns true if we send a packet to notify client.
fals otherwise (e.g. the socket is dead).
"""
ttl = self.live_after - time.time()
if ttl > 0:
logger.debug(f"sleeping {ttl:.2f}s until client {self.addr_info} is ready to receive notification")
time.sleep(ttl)
try:
self.sock.sendall(message)
except Exception as e:
logger.warning(f"failed to notify client {self.addr_info} {e}")
return False
else:
logger.info(f"successfully notified {self.addr_info}: {message}")
return True
class Adapter:
def __init__(self, host: str, port: int, silence: int, topic: str):
self.host = host
self.port = port
self.silence = silence
self.topic = topic
self.clients = set()
def log_clients(self):
clients_str = '\n'.join(f' {c.addr_info}' for c in self.clients)
logger.debug(f"clients alive ({len(self.clients)}):\n{clients_str}")
def add_client(self, client: Client):
# it's a little bit risky to keep more than one client at the same IP address,
# because it's possible a notification comes in and we ring the old connection,
# even when the new connection says "don't ring yet".
for c in set(self.clients):
if c.addr_info[0] == client.addr_info[0]:
logger.info(f"purging old client before adding new one at same address: {c.addr_info} -> {client.addr_info}")
self.clients.remove(c)
logger.info(f"accepted client at {client.addr_info}")
self.clients.add(client)
def listener_loop(self):
logger.info(f"listening for connections on {self.host}:{self.port}")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((self.host, self.port))
s.listen(LISTEN_QUEUE)
while True:
conn, addr_info = s.accept()
self.add_client(Client(conn, addr_info, live_after = time.time() + self.silence))
def notify_clients(self, message: bytes = WAKE_MESSAGE):
# notify every client, and drop any which have disconnected.
# note that we notify based on age (oldest -> youngest)
# because notifying young clients might entail sleeping until they're ready.
clients = sorted(self.clients, key=lambda c: (c.live_after, c.addr_info))
dead_clients = [
c for c in clients if not c.try_notify(message)
]
for c in dead_clients:
self.clients.remove(c)
self.log_clients()
def notify_loop(self):
logger.info("waiting for notification events")
ntfy_proc = subprocess.Popen(
[
"ntfy",
"sub",
f"https://ntfy.uninsane.org/{self.topic}"
],
stdout=subprocess.PIPE
)
for line in iter(ntfy_proc.stdout.readline, b''):
logger.debug(f"received notification: {line}")
self.notify_clients()
def get_topic() -> str:
return open('/run/secrets/ntfy-sh-topic', 'rt').read().strip()
def run_forever(callable):
try:
callable()
except Exception as e:
logger.error(f"{callable} failed: {e}")
else:
logger.error(f"{callable} unexpectedly returned")
# sys.exit(1)
os._exit(1) # sometimes `sys.exit()` doesn't actually exit...
def main():
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
parser = argparse.ArgumentParser(description="accept connections and notify the other end upon ntfy activity, with a guaranteed amount of silence")
parser.add_argument('--verbose', action='store_true')
parser.add_argument('--host', type=str, default='')
parser.add_argument('--port', type=int)
parser.add_argument('--silence', type=int, help="number of seconds to remain silent upon accepting a connection")
args = parser.parse_args()
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
adapter = Adapter(args.host, args.port, args.silence, get_topic())
listener_loop = threading.Thread(target=run_forever, name="listener_loop", args=(adapter.listener_loop,))
notify_loop = threading.Thread(target=run_forever, name="notify_loop", args=(adapter.notify_loop,))
# TODO: this method of exiting seems to sometimes leave the listener behind (?)
# preventing anyone else from re-binding the port.
listener_loop.start()
notify_loop.start()
listener_loop.join()
notify_loop.join()
if __name__ == '__main__':
main()

View File

@@ -1,72 +0,0 @@
# service which adapts ntfy-sh into something suitable specifically for the Pinephone's
# wake-on-lan (WoL) feature.
# notably, it provides a mechanism by which the caller can be confident of an interval in which
# zero traffic will occur on the TCP connection, thus allowing it to enter sleep w/o fear of hitting
# race conditions in the Pinephone WoL feature.
{ config, lib, pkgs, ... }:
let
cfg = config.sane.ntfy-waiter;
portLow = 5550;
portHigh = 5559;
portRange = lib.range portLow portHigh;
numPorts = portHigh - portLow + 1;
mkService = port: let
silence = port - portLow;
flags = lib.optional cfg.verbose "--verbose";
cli = [
"${cfg.package}/bin/ntfy-waiter"
"--port"
"${builtins.toString port}"
"--silence"
"${builtins.toString silence}"
] ++ flags;
in {
"ntfy-waiter-${builtins.toString silence}" = {
# TODO: run not as root (e.g. as ntfy-sh)
description = "wait for notification, with ${builtins.toString silence} seconds of guaranteed silence";
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "5s";
ExecStart = lib.concatStringsSep " " cli;
};
after = [ "network.target" ];
wantedBy = [ "default.target" ];
};
};
in
{
options = with lib; {
sane.ntfy-waiter.enable = mkOption {
type = types.bool;
default = true;
};
sane.ntfy-waiter.verbose = mkOption {
type = types.bool;
default = true;
};
sane.ntfy-waiter.package = mkOption {
type = types.package;
default = pkgs.static-nix-shell.mkPython3Bin {
pname = "ntfy-waiter";
src = ./.;
pkgs = [ "ntfy-sh" ];
};
description = ''
exposed to provide an attr-path by which one may build the package for manual testing.
'';
};
};
config = lib.mkIf cfg.enable {
sane.ports.ports = lib.mkMerge (lib.forEach portRange (port: {
"${builtins.toString port}" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-notification-waiter-${builtins.toString (port+1)}-of-${builtins.toString numPorts}";
};
}));
systemd.services = lib.mkMerge (builtins.map mkService portRange);
};
}

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.byStore.plaintext = lib.mkIf cfg.enable [
{ user = "pict-rs"; group = "pict-rs"; path = 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,21 +1,14 @@
# docs:
# - <https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix>
# - <https://docs.pleroma.social/backend/configuration/cheatsheet/>
# example config:
# - <https://git.pleroma.social/pleroma/pleroma/-/blob/develop/config/config.exs>
# - https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
# - https://docs.pleroma.social/backend/configuration/cheatsheet/
#
# to run it in a oci-container: <https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix>
#
# admin frontend: <https://fed.uninsane.org/pleroma/admin>
# to run it in a oci-container: https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix
{ config, pkgs, ... }:
let
logLevel = "warn";
# logLevel = "debug";
in
{
sane.persist.sys.byStore.plaintext = [
{ user = "pleroma"; group = "pleroma"; path = "/var/lib/pleroma"; }
sane.persist.sys.plaintext = [
# TODO: mode? could be more granular
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
];
services.pleroma.enable = true;
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
@@ -63,7 +56,6 @@ in
database: "pleroma",
hostname: "localhost",
pool_size: 10,
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
@@ -104,22 +96,10 @@ in
backends: [{ExSyslogger, :ex_syslogger}]
config :logger, :ex_syslogger,
level: :${logLevel}
# policies => list of message rewriting facilities to be enabled
# transparence => whether to publish these rules in node_info (and /about)
config :pleroma, :mrf,
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy],
transparency: true
# reject => { host, reason }
config :pleroma, :mrf_simple,
reject: [ {"threads.net", "megacorp"}, {"*.threads.net", "megacorp"} ]
# reject: [ [host: "threads.net", reason: "megacorp"], [host: "*.threads.net", reason: "megacorp"] ]
level: :warn
# level: :debug
# XXX colin: not sure if this actually _does_ anything
# better to steal emoji from other instances?
# - <https://docs.pleroma.social/backend/configuration/cheatsheet/#mrf_steal_emoji>
config :pleroma, :emoji,
shortcode_globs: ["/emoji/**/*.png"],
groups: [
@@ -168,7 +148,6 @@ in
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4000";
recommendedProxySettings = true;
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''
# XXX colin: this block is in the nixos examples: i don't understand all of it
@@ -187,24 +166,23 @@ in
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# # proxy_set_header Host $http_host;
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# colin: added this due to Pleroma complaining in its logs
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Proto $scheme;
# NB: this defines the maximum upload size
client_max_body_size 16m;
'';
};
};
sane.dns.zones."uninsane.org".inet.CNAME."fed" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
sops.secrets."pleroma_secrets" = {
owner = config.users.users.pleroma.name;

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 = {
@@ -18,46 +19,39 @@ let
};
in
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode? could be more granular
{ user = "opendkim"; group = "opendkim"; path = "/var/lib/opendkim"; }
{ user = "root"; group = "root"; path = "/var/lib/postfix"; }
{ user = "root"; group = "root"; path = "/var/spool/mail"; }
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
{ user = "root"; group = "root"; directory = "/var/spool/mail"; }
# *probably* don't need these dirs:
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
# "/var/lib/dovecot"
];
# XXX(2023/10/20): opening these ports in the firewall has the OPPOSITE effect as intended.
# these ports are only routable so long as they AREN'T opened.
# probably some cursed interaction with network namespaces introduced after 2023/10/10.
# sane.ports.ports."25" = {
# protocol = [ "tcp" ];
# # XXX visibleTo.lan effectively means "open firewall, but don't configure any NAT/forwarding"
# visibleTo.lan = true;
# description = "colin-smtp-mx.uninsane.org";
# };
# sane.ports.ports."465" = {
# protocol = [ "tcp" ];
# visibleTo.lan = true;
# description = "colin-smtps-mx.uninsane.org";
# };
# sane.ports.ports."587" = {
# protocol = [ "tcp" ];
# visibleTo.lan = 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;
};
sane.dns.zones."uninsane.org".inet = {
sane.services.trust-dns.zones."uninsane.org".inet = {
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
@@ -68,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>:
@@ -101,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;
@@ -149,8 +121,6 @@ in
};
#### OPENDKIM
services.opendkim.enable = true;
# services.opendkim.domains = "csl:uninsane.org";
services.opendkim.domains = "uninsane.org";
@@ -174,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;
@@ -195,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

@@ -1,39 +1,12 @@
{ pkgs, ... }:
{ ... }:
let
GiB = n: MiB 1024*n;
MiB = n: KiB 1024*n;
KiB = n: 1024*n;
in
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode?
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; }
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
];
services.postgresql.enable = true;
# HOW TO UPDATE:
# postgres version updates are manual and require intervention.
# - `sane-stop-all-servo`
# - `systemctl start postgresql`
# - as `sudo su postgres`:
# - `cd /var/log/postgresql`
# - `pg_dumpall > state.sql`
# - `echo placeholder > <new_version>` # to prevent state from being created earlier than we want
# - then, atomically:
# - update the `services.postgresql.package` here
# - `dataDir` is atomically updated to match package; don't touch
# - `nixos-rebuild --flake . switch ; sane-stop-all-servo`
# - `sudo rm -rf /var/lib/postgresql/<new_version>`
# - `systemctl start postgresql`
# - as `sudo su postgres`:
# - `cd /var/lib/postgreql`
# - `psql -f state.sql`
# - restart dependent services (maybe test one at a time)
services.postgresql.package = pkgs.postgresql_15;
# services.postgresql.dataDir = "/opt/postgresql/13";
# XXX colin: for a proper deploy, we'd want to include something for Pleroma here too.
# services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
# CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD '<password goes here>';
@@ -44,33 +17,10 @@ in
# LC_CTYPE = "C";
# '';
# perf tuning
# TODO: perf tuning
# - for recommended values see: <https://pgtune.leopard.in.ua/>
# - for official docs (sparse), see: <https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
services.postgresql.settings = {
# DB Version: 15
# OS Type: linux
# DB Type: web
# Total Memory (RAM): 32 GB
# CPUs num: 12
# Data Storage: ssd
max_connections = 200;
shared_buffers = "8GB";
effective_cache_size = "24GB";
maintenance_work_mem = "2GB";
checkpoint_completion_target = 0.9;
wal_buffers = "16MB";
default_statistics_target = 100;
random_page_cost = 1.1;
effective_io_concurrency = 200;
work_mem = "10485kB";
min_wal_size = "1GB";
max_wal_size = "4GB";
max_worker_processes = 12;
max_parallel_workers_per_gather = 4;
max_parallel_workers = 12;
max_parallel_maintenance_workers = 4;
};
# services.postgresql.settings = { ... }
# daily backups to /var/backup
services.postgresqlBackup.enable = true;

View File

@@ -0,0 +1,64 @@
# example configs:
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
# create users with:
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
{ lib, ... }:
# XXX disabled: doesn't send messages to nixnet.social (only receives them).
# nixnet runs ejabberd, so revisiting that.
lib.mkIf false
{
sane.persist.sys.plaintext = [
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
];
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" ];
security.acme.certs."uninsane.org".extraDomainNames = [
"conference.xmpp.uninsane.org"
"upload.xmpp.uninsane.org"
];
services.prosody = {
enable = true;
admins = [ "colin@uninsane.org" ];
# allowRegistration = false;
# extraConfig = ''
# s2s_require_encryption = true
# c2s_require_encryption = true
# '';
extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
muc = [
{
domain = "conference.xmpp.uninsane.org";
}
];
uploadHttp.domain = "upload.xmpp.uninsane.org";
virtualHosts = {
localhost = {
domain = "localhost";
enabled = true;
};
"xmpp.uninsane.org" = {
domain = "uninsane.org";
enabled = true;
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
};
};
};
}

View File

@@ -1,289 +0,0 @@
# example configs:
# - official: <https://prosody.im/doc/example_config>
# - nixos: <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
# config options:
# - <https://prosody.im/doc/configure>
#
# modules:
# - main: <https://prosody.im/doc/modules>
# - community: <https://modules.prosody.im/index.html>
#
# debugging:
# - logging:
# - enable `stanza_debug` module
# - enable `log.debug = "*syslog"` in extraConfig
# - interactive:
# - `telnet localhost 5582` (this is equal to `prosodyctl shell` -- but doesn't hang)
# - `watch:stanzas(target_spec, filter)` -> to log stanzas, for version > 0.12
# - console docs: <https://prosody.im/doc/console>
# - can modify/inspect arbitrary internals (lua) by prefixing line with `> `
# - e.g. `> _G` to print all globals
#
# sanity checks:
# - `sudo -u prosody -g prosody prosodyctl check connectivity`
# - `sudo -u prosody -g prosody prosodyctl check turn`
# - `sudo -u prosody -g prosody prosodyctl check turn -v --ping=stun.conversations.im`
# - checks that my stun/turn server is usable by clients of conversations.im (?)
# - `sudo -u prosody -g prosody prosodyctl check` (dns, config, certs)
#
#
# create users with:
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
#
#
# federation/support matrix:
# - nixnet.services (runs ejabberd):
# - WORKS: sending and receiving PMs and calls (2023/10/15)
# - N.B.: it didn't originally work; was solved by disabling the lua-unbound DNS option & forcing the system/local resolver
# - cheogram (XMPP <-> SMS gateway):
# - WORKS: sending and receiving PMs, images (2023/10/15)
# - PARTIAL: calls (xmpp -> tel works; tel -> xmpp fails)
# - maybe i need to setup stun/turn
#
# TODO:
# - enable push notifications (mod_cloud_notify)
# - optimize coturn (e.g. move off of the VPN!)
# - ensure muc is working
# - enable file uploads
# - "upload.xmpp.uninsane.org:http_upload: URL: <https://upload.xmpp.uninsane.org:5281/upload> - Ensure this can be reached by users"
# - disable or fix bosh (jabber over http):
# - "certmanager: No certificate/key found for client_https port 0"
{ lib, pkgs, ... }:
let
# enables very verbose logging
enableDebug = false;
in
{
sane.persist.sys.byStore.plaintext = [
{ user = "prosody"; group = "prosody"; path = "/var/lib/prosody"; }
];
sane.ports.ports."5000" = {
protocol = [ "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
description = "colin-xmpp-prosody-fileshare-proxy65";
};
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-prosody-https"; # necessary?
};
users.users.prosody.extraGroups = [
"nginx" # provide access to certs
"ntfy-sh" # access to secret ntfy topic
];
security.acme.certs."uninsane.org".extraDomainNames = [
"xmpp.uninsane.org"
"conference.xmpp.uninsane.org"
"upload.xmpp.uninsane.org"
];
# exists so the XMPP server's cert can obtain altNames for all its resources
services.nginx.virtualHosts."xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."conference.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."upload.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
sane.dns.zones."uninsane.org".inet = {
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
A."xmpp" = "%ANATIVE%";
CNAME."conference.xmpp" = "xmpp";
CNAME."upload.xmpp" = "xmpp";
# _Service._Proto.Name TTL Class SRV Priority Weight Port Target
# - <https://xmpp.org/extensions/xep-0368.html>
# something's requesting the SRV records for conference.xmpp, so let's include it
# nothing seems to request XMPP SRVs for the other records (except @)
# lower numerical priority field tells clients to prefer this method
SRV."_xmpps-client._tcp.conference.xmpp" = "3 50 5223 xmpp";
SRV."_xmpps-server._tcp.conference.xmpp" = "3 50 5270 xmpp";
SRV."_xmpp-client._tcp.conference.xmpp" = "5 50 5222 xmpp";
SRV."_xmpp-server._tcp.conference.xmpp" = "5 50 5269 xmpp";
SRV."_xmpps-client._tcp" = "3 50 5223 xmpp";
SRV."_xmpps-server._tcp" = "3 50 5270 xmpp";
SRV."_xmpp-client._tcp" = "5 50 5222 xmpp";
SRV."_xmpp-server._tcp" = "5 50 5269 xmpp";
};
# help Prosody find its certificates.
# pointing it to /var/lib/acme doesn't quite work because it expects the private key
# to be named `privkey.pem` instead of acme's `key.pem`
# <https://prosody.im/doc/certificates#automatic_location>
sane.fs."/etc/prosody/certs/uninsane.org/fullchain.pem" = {
symlink.target = "/var/lib/acme/uninsane.org/fullchain.pem";
wantedBeforeBy = [ "prosody.service" ];
};
sane.fs."/etc/prosody/certs/uninsane.org/privkey.pem" = {
symlink.target = "/var/lib/acme/uninsane.org/key.pem";
wantedBeforeBy = [ "prosody.service" ];
};
services.prosody = {
enable = true;
package = pkgs.prosody.override {
# XXX(2023/10/15): build without lua-unbound support.
# this forces Prosody to fall back to the default Lua DNS resolver, which seems more reliable.
# fixes errors like "unbound.queryXYZUV: Resolver error: out of memory"
# related: <https://issues.prosody.im/1737#comment-11>
lua.withPackages = selector: pkgs.lua.withPackages (p:
selector (p // { luaunbound = null; })
);
# withCommunityModules = [ "turncredentials" ];
};
admins = [ "colin@uninsane.org" ];
# allowRegistration = false; # defaults to false
muc = [
{
domain = "conference.xmpp.uninsane.org";
}
];
uploadHttp.domain = "upload.xmpp.uninsane.org";
virtualHosts = {
# "Prosody requires at least one enabled VirtualHost to function. You can
# safely remove or disable 'localhost' once you have added another."
# localhost = {
# domain = "localhost";
# enabled = true;
# };
"xmpp.uninsane.org" = {
domain = "uninsane.org";
enabled = true;
};
};
## modules:
# these are enabled by default, via <repo:nixos/nixpkgs:/pkgs/servers/xmpp/prosody/default.nix>
# - cloud_notify
# - http_upload
# - vcard_muc
# these are enabled by the module defaults (services.prosody.modules.<foo>)
# - admin_adhoc
# - blocklist
# - bookmarks
# - carbons
# - cloud_notify
# - csi
# - dialback
# - disco
# - http_files
# - mam
# - pep
# - ping
# - private
# - XEP-0049: let clients store arbitrary (private) data on the server
# - proxy65
# - XEP-0065: allow server to proxy file transfers between two clients who are behind NAT
# - register
# - roster
# - saslauth
# - smacks
# - time
# - tls
# - uptime
# - vcard_legacy
# - version
extraPluginPaths = [ ./modules ];
extraModules = [
# admin_shell: allows `prosodyctl shell` to work
# see: <https://prosody.im/doc/modules/mod_admin_shell>
# see: <https://prosody.im/doc/console>
"admin_shell"
"admin_telnet" #< needed by admin_shell
# lastactivity: XEP-0012: allow users to query how long another user has been idle for
# - not sure why i enabled this; think it was in someone's config i referenced
"lastactivity"
# allows prosody to share TURN/STUN secrets with XMPP clients to provide them access to the coturn server.
# see: <https://prosody.im/doc/coturn>
"turn_external"
# legacy coturn integration
# see: <https://modules.prosody.im/mod_turncredentials.html>
# "turncredentials"
"sane_ntfy"
] ++ lib.optionals enableDebug [
"stanza_debug" #< logs EVERY stanza as debug: <https://prosody.im/doc/modules/mod_stanza_debug>
];
extraConfig = ''
local function readAll(file)
local f = assert(io.open(file, "rb"))
local content = f:read("*all")
f:close()
-- remove trailing newline
return string.gsub(content, "%s+", "")
end
-- logging docs:
-- - <https://prosody.im/doc/logging>
-- - <https://prosody.im/doc/advanced_logging>
-- levels: debug, info, warn, error
log = {
${if enableDebug then "debug" else "info"} = "*syslog";
}
-- see: <https://prosody.im/doc/certificates#automatic_location>
-- try to solve: "certmanager: Error indexing certificate directory /etc/prosody/certs: cannot open /etc/prosody/certs: No such file or directory"
-- only, this doesn't work because prosody doesn't like acme's naming scheme
-- certificates = "/var/lib/acme"
c2s_direct_tls_ports = { 5223 }
s2s_direct_tls_ports = { 5270 }
turn_external_host = "turn.uninsane.org"
turn_external_secret = readAll("/var/lib/coturn/shared_secret.bin")
-- turn_external_user = "prosody"
-- legacy mod_turncredentials integration
-- turncredentials_host = "turn.uninsane.org"
-- turncredentials_secret = readAll("/var/lib/coturn/shared_secret.bin")
ntfy_binary = "${pkgs.ntfy-sh}/bin/ntfy"
ntfy_topic = readAll("/run/secrets/ntfy-sh-topic")
-- s2s_require_encryption = true
-- c2s_require_encryption = true
'';
};
}

View File

@@ -1,52 +0,0 @@
-- simple proof-of-concept Prosody module
-- module development guide: <https://prosody.im/doc/developers/modules>
-- module API docs: <https://prosody.im/doc/developers/moduleapi>
--
-- much of this code is lifted from Prosody's own `mod_cloud_notify`
local jid = require"util.jid";
local ntfy = module:get_option_string("ntfy_binary", "ntfy");
local ntfy_topic = module:get_option_string("ntfy_topic", "xmpp");
module:log("info", "initialized");
local function is_urgent(stanza)
if stanza.name == "message" then
if stanza:get_child("propose", "urn:xmpp:jingle-message:0") then
return true, "jingle call";
end
end
end
local function publish_ntfy(message)
-- message should be the message to publish
local ntfy_url = string.format("https://ntfy.uninsane.org/%s", ntfy_topic)
local cmd = string.format("%s pub %q %q", ntfy, ntfy_url, message)
module.log("debug", "invoking ntfy: %s", cmd)
local success, reason, code = os.execute(cmd)
if not success then
module:log("warn", "ntfy failed: %s => %s %d", cmd, reason, code)
end
end
local function archive_message_added(event)
-- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id }
local stanza = event.stanza;
local to = stanza.attr.to;
to = to and jid.split(to) or event.origin.username;
-- only notify if the stanza destination is the mam user we store it for
if event.for_user == to then
local is_urgent_stanza, urgent_reason = is_urgent(event.stanza);
if is_urgent_stanza then
module:log("info", "urgent push for %s (%s)", to, urgent_reason);
publish_ntfy(urgent_reason)
end
end
end
module:hook("archive-message-added", archive_message_added);

View File

@@ -1,27 +1,12 @@
{ config, pkgs, ... }:
{ pkgs, ... }:
{
sane.persist.sys.byStore.plaintext = [
sane.persist.sys.plaintext = [
# TODO: mode? we need this specifically for the stats tracking in .config/
{ user = "transmission"; group = config.users.users.transmission.group; path = "/var/lib/transmission"; }
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
];
users.users.transmission.extraGroups = [ "media" ];
services.transmission.enable = true;
services.transmission.package = pkgs.transmission_4; #< 2023/09/06: nixpkgs `transmission` defaults to old 3.00
#v setting `group` this way doesn't tell transmission to `chown` the files it creates
# it's a nixpkgs setting which just runs the transmission daemon as this group
services.transmission.group = "media";
# transmission will by default not allow the world to read its files.
services.transmission.downloadDirPermissions = "775";
services.transmission.extraFlags = [
"--log-level=debug"
];
services.transmission.settings = {
# message-level = 3; #< enable for debug logging. 0-3, default is 2.
# 0.0.0.0 => allow rpc from any host: we gate it via firewall and auth requirement
rpc-bind-address = "0.0.0.0";
#rpc-host-whitelist = "bt.uninsane.org";
#rpc-whitelist = "*.*.*.*";
@@ -32,8 +17,9 @@
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
rpc-whitelist-enabled = false;
# download-dir = "/opt/uninsane/media/";
# hopefully, make the downloads world-readable
# umask = 0; #< default is 2: i.e. deny writes from world
umask = 0;
# force peer connections to be encrypted
encryption = 2;
@@ -41,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
@@ -49,18 +35,17 @@
download-dir = "/var/lib/uninsane/media";
incomplete-dir = "/var/lib/uninsane/media/incomplete";
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
# TODO: uncomment this line!
incomplete-dir-enabled = false;
};
# transmission will by default not allow the world to read its files.
services.transmission.downloadDirPermissions = "775";
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
Restart = "on-failure";
RestartSec = "30s";
LogLevelMax = "warning";
};
# service to automatically backup torrents i add to transmission
@@ -90,6 +75,6 @@
};
};
sane.dns.zones."uninsane.org".inet.CNAME."bt" = "native";
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."bt" = "native";
}

View File

@@ -1,27 +1,17 @@
# TODO: split this file apart into smaller files to make it easier to understand
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
let
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
bindOvpn = "10.0.1.5";
in lib.mkMerge [
{
services.trust-dns.enable = true;
sane.services.trust-dns.enable = true;
# don't bind to IPv6 until i explicitly test that stack
services.trust-dns.settings.listen_addrs_ipv6 = [];
services.trust-dns.quiet = true;
# services.trust-dns.debug = true;
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.
"192.168.0.5"
"10.0.1.5"
];
sane.services.trust-dns.quiet = true;
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
visibleTo.wan = true;
visibleTo.ovpn = true;
description = "colin-dns-hosting";
};
sane.dns.zones."uninsane.org".TTL = 900;
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
# SOA record structure: <https://en.wikipedia.org/wiki/SOA_record#Structure>
# SOA MNAME RNAME (... rest)
@@ -31,30 +21,26 @@ in lib.mkMerge [
# Refresh = how frequently secondary NS should query master
# Retry = how long secondary NS should wait until re-querying master after a failure (must be < Refresh)
# Expire = how long secondary NS should continue to reply to queries after master fails (> Refresh + Retry)
sane.dns.zones."uninsane.org".inet = {
sane.services.trust-dns.zones."uninsane.org".inet = {
SOA."@" = ''
ns1.uninsane.org. admin-dns.uninsane.org. (
2023092101 ; Serial
2022122101 ; Serial
4h ; Refresh
30m ; Retry
7d ; Expire
5m) ; Negative response TTL
'';
TXT."rev" = "2023092101";
CNAME."native" = "%CNAMENATIVE%";
A."@" = "%ANATIVE%";
A."servo.wan" = "%AWAN%";
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.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."
@@ -62,147 +48,20 @@ in lib.mkMerge [
];
};
services.trust-dns.settings.zones = [ "uninsane.org" ];
sane.services.trust-dns.zones."uninsane.org".file =
"/var/lib/trust-dns/uninsane.org.zone";
# TODO: can i transform this into some sort of service group?
# have `systemctl restart trust-dns.service` restart all the individual services?
systemd.services.trust-dns.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "trust-dns";
Group = "trust-dns";
wantedBy = lib.mkForce [];
};
systemd.services.trust-dns.enable = false;
users.groups.trust-dns = {};
users.users.trust-dns = {
group = "trust-dns";
isSystemUser = true;
};
# 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?
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
# - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace";
};
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
}
{
systemd.services =
let
sed = "${pkgs.gnused}/bin/sed";
stateDir = "/var/lib/trust-dns";
zoneTemplate = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
zoneDirFor = flavor: "${stateDir}/${flavor}";
zoneFor = flavor: "${zoneDirFor flavor}/uninsane.org.zone";
mkTrustDnsService = opts: flavor: let
flags = let baseCfg = config.services.trust-dns; in
(lib.optional baseCfg.debug "--debug") ++ (lib.optional baseCfg.quiet "--quiet");
flagsStr = builtins.concatStringsSep " " flags;
anative = nativeAddrs."servo.${flavor}";
toml = pkgs.formats.toml { };
configTemplate = opts.config or (toml.generate "trust-dns-${flavor}.toml" (
(
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
) // {
listen_addrs_ipv4 = opts.listen or [ anative ];
}
));
configFile = "${stateDir}/${flavor}-config.toml";
port = opts.port or 53;
in {
description = "trust-dns Domain Name Server (serving ${flavor})";
unitConfig.Documentation = "https://trust-dns.org/";
preStart = ''
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
${sed} s/%AWAN%/$wan/ ${configTemplate} > ${configFile}
'' + lib.optionalString (!opts ? config) ''
mkdir -p ${zoneDirFor flavor}
${sed} \
-e s/%CNAMENATIVE%/servo.${flavor}/ \
-e s/%ANATIVE%/${anative}/ \
-e s/%AWAN%/$wan/ \
-e s/%AOVPNS%/185.157.162.178/ \
${zoneTemplate} > ${zoneFor flavor}
'';
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
ExecStart = ''
${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram} \
--port ${builtins.toString port} \
--zonedir ${zoneDirFor flavor}/ \
--config ${configFile} ${flagsStr}
'';
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
in {
trust-dns-wan = mkTrustDnsService { listen = [ nativeAddrs."servo.lan" bindOvpn ]; } "wan";
trust-dns-lan = mkTrustDnsService { port = 1053; } "lan";
trust-dns-hn = mkTrustDnsService { port = 1053; } "hn";
trust-dns-hn-resolver = mkTrustDnsService {
config = pkgs.writeText "hn-resolver-config.toml" ''
# i host a resolver in the wireguard VPN so that clients can resolve DNS through the VPN.
# (that's what this file achieves).
#
# one would expect this resolver could host the authoritative zone for `uninsane.org`, and then forward everything else to the system resolver...
# and while that works for `dig`, it breaks for `nslookup` (and so `ssh`, etc).
#
# DNS responses include a flag for if the responding server is the authority of the zone queried.
# it seems that default Linux stub resolvers either:
# - expect DNSSEC when the response includes that bit, or
# - expect A records to be in the `answer` section instead of `additional` section.
# or perhaps something more nuanced. but for `nslookup` to be reliable, it has to talk to an
# instance of trust-dns which is strictly a resolver, with no authority.
# hence, this config: a resolver which forwards to the actual authority.
listen_addrs_ipv4 = ["${nativeAddrs."servo.hn"}"]
listen_addrs_ipv6 = []
[[zones]]
zone = "uninsane.org"
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "${nativeAddrs."servo.hn"}:1053", protocol = "udp", trust_nx_responses = true }] }
[[zones]]
# forward the root zone to the local DNS resolver
zone = "."
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "127.0.0.53:53", protocol = "udp", trust_nx_responses = true }] }
'';
} "hn-resolver";
};
sane.services.dyn-dns.restartOnChange = [
"trust-dns-wan.service"
"trust-dns-lan.service"
"trust-dns-hn.service"
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
];
}
]

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

@@ -0,0 +1,996 @@
# cross compiling
# - for edge-casey things, see in nixpkgs:
# - `git show da9a9a440415b236f22f57ba67a24ab3fb53f595`
# - e.g. `mesonEmulatorHook`, `depsBuildBuild`, `python3.pythonForBuild`
# - <doc/stdenv/cross-compilation.chapter.md>
# - e.g. `makeFlags = [ "CC=${stdenv.cc.targetPrefix}cc" ];`
#
# build a particular package as evaluated here with:
# - toplevel: `nix build '.#host-pkgs.moby-cross.xdg-utils'`
# - scoped: `nix build '.#host-pkgs.moby-cross.gnome.mutter'`
# - python: `nix build '.#host-pkgs.moby-cross.python310Packages.pandas'`
# - perl: `nix build '.#host-pkgs.moby-cross.perl536Packages.ModuleBuild'`
# - qt: `nix build '.#host-pkgs.moby-cross.qt5.qtbase'`
# - qt: `nix build '.#host-pkgs.moby-cross.libsForQt5.phonon'`
# most of these can be built in a nixpkgs source root like:
# - `nix build '.#pkgsCross.aarch64-multiplatform.xdg-utils'`
# - `nix build '.#pkgsCross.gnu64.xdg-utils'` # for x86_64-linux
#
# tracking issues, PRs:
# - libuv tests fail: <https://github.com/NixOS/nixpkgs/issues/190807>
# - last checked: 2023-02-07
# - opened: 2022-09-11
# - perl Module Build broken: <https://github.com/NixOS/nixpkgs/issues/66741>
# - last checked: 2023-02-07
# - opened: 2019-08
# - ModuleBuild needs access to `Scalar/Utils.pm`, which doesn't *seem* to exist in the cross builds
# - this can be fixed by adding `nativeBuildInputs = [ perl ]` to it
# - alternatively, there's some "stubbing" method mentioned in <pkgs/development/interpreters/perl/default.nix>
# - stubbing documented at bottom: <nixpkgs:doc/languages-frameworks/perl.section.md>
#
# - perl536Packages.Testutf8 fails to cross: <https://github.com/NixOS/nixpkgs/issues/198548>
# - last checked: 2023-02-07
# - opened: 2022-10
# - python310Packages.psycopg2: <https://github.com/NixOS/nixpkgs/issues/210265>
# - last checked: 2023-02-06
# - i have a potential fix:
# """
# i was able to just add `postgresql` to the `buildInputs` (so that it's in both `buildInputs` and `nativeBuildInputs`):
# it fixed the build for `pkgsCross.aarch64-multiplatform.python310Packages.psycopg2` but not for `armv7l-hf-multiplatform` that this issue description calls out.
#
# also i haven't deployed it yet to make sure this doesn't cause anything funky at runtime though.
# """
# TODO:
# - ??.llvmPackages_14.llvm: "FAIL: LLVM-Unit :: ExecutionEngine/MCJIT/./MCJITTests/MCJITMultipleModuleTest.two_module_global_variables_case (43769 of 46988)"
# - nix log /nix/store/ib2yw6sajnhlmibxkrn7lj7chllbr85h-llvm-14.0.6.drv
# - wanted by clang-11-12-LLVMgold-path, compiler-rt-libc-12.0.1, clang-wrapper-12.0.1
# - ?..llvmPackages_12.llvm: "FAIL: LLVM-Unit :: ExecutionEngine/MCJIT/./MCJITTests/MCJITTest.return_global (2857 of 42084)"
# - nix log /nix/store/6vydavlxh1gvs0vmrkcx9qp67g3h7kcz-llvm-12.0.1.drv
# - wanted by sequoia, rav1e, rustc-1.66.1
# - `host-pkgs.desko.stdenv` fails build:
# - #cross-compiling:nixos.org says pkgsCross.gnu64 IS KNOWN TO NOT COMPILE. let this go for now:
# - make a `<machine>` (don't specifiy local/targetSystem) and `<machine>-cross` target.
# - `desko-cross` will be broken but `desko` can work
# - see <nixpkgs:pkgs/stdenv/linux/default.nix>
# - disallowedRequisites = [ bootstrapTools.out ];
# """
# error: output '/nix/store/w2vgzyvs2jzf7yr6qqqrjbvrqxxmhwy0-stdenv-linux' is not allowed to refer to the following paths:
# /nix/store/2qbgchkjj1hqi1c8raznwml94pkm3k7q-libunistring-1.0
# /nix/store/4j425ybkjxcdj89352l5gpdl3nmxq4zn-libidn2-2.3.2
# /nix/store/c35hf8g5b9vksadym9dbjrd6p2y11m8h-glibc-2.35-224
# /nix/store/qbgfsaviwqi2p6jr7an1g2754sv3xqhn-gcc-11.3.0-lib
# """
# - rg doesn't reveal any such references in the output though...
# - nor references to bootstrapTools
# - HOWEVER, IT DOES CONTAIN A REFERENCE TO THE PREVIOUS STAGE'S BASH:
# - /nix/store/w2vgzyvs2jzf7yr6qqqrjbvrqxxmhwy0-stdenv-linux/setup
# - export SHELL=/nix/store/qqa28hmysc23yy081d178jfd9a1yk8aw-bash-5.2-p15/bin/bash
# - not clear if that matters? but maybe it reaches bootstrapTools transitively?
# - yeah: that bash specifies the above `glibc` as its loader
# - so we probably can't `inherit` the emulated bash like that.
# - try building `.#host-pkgs.desko.stdenv.shellPackage` or `.#host-pkgs.desko.stdenv.bootstrapTools`
# - `file result/bin/bash` does show that it uses the interpreter for the glibc, above
{ config, lib, pkgs, ... }:
let
# these are the overlays which we *also* pass through to the cross and emulated package sets.
# TODO: refactor to not specify same overlay in multiple places (here and flake.nix).
overlays = [
(import ./../../overlays/pkgs.nix)
(import ./../../overlays/pins.nix)
];
mkCrossFrom = localSystem: pkgs:
import pkgs.path {
inherit localSystem; # localSystem is equivalent to buildPlatform
crossSystem = pkgs.stdenv.hostPlatform.system;
inherit (config.nixpkgs) config;
inherit overlays;
};
mkEmulated = pkgs:
import pkgs.path {
# system = pkgs.stdenv.hostPlatform.system;
localSystem = pkgs.stdenv.hostPlatform.system;
inherit (config.nixpkgs) config;
inherit overlays;
};
in
{
# options = {
# perlPackageOverrides = lib.mkOption {
# };
# };
config = {
# the configuration of which specific package set `pkgs.cross` refers to happens elsewhere;
# here we just define them all.
nixpkgs.config.perlPackageOverrides = pkgs: (with pkgs; with pkgs.perlPackages; {
# these are the upstream nixpkgs perl modules, but with `nativeBuildInputs = [ perl ]`
# to fix cross compilation errors
ModuleBuild = buildPerlPackage {
pname = "Module-Build";
version = "0.4231";
src = fetchurl {
url = "mirror://cpan/authors/id/L/LE/LEONT/Module-Build-0.4231.tar.gz";
hash = "sha256-fg9MaSwXQMGshOoU1+o9i8eYsvsmwJh3Ip4E9DCytxc=";
};
# support cross-compilation by removing unnecessary File::Temp version check
# postPatch = lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) ''
# sed -i '/File::Temp/d' Build.PL
# '';
nativeBuildInputs = [ perl ];
meta = {
description = "Build and install Perl modules";
license = with lib.licenses; [ artistic1 gpl1Plus ];
mainProgram = "config_data";
};
};
FileBaseDir = buildPerlModule {
version = "0.08";
pname = "File-BaseDir";
src = fetchurl {
url = "mirror://cpan/authors/id/K/KI/KIMRYAN/File-BaseDir-0.08.tar.gz";
hash = "sha256-wGX80+LyKudpk3vMlxuR+AKU1QCfrBQL+6g799NTBeM=";
};
configurePhase = ''
runHook preConfigure
perl Build.PL PREFIX="$out" prefix="$out"
'';
nativeBuildInputs = [ perl ];
propagatedBuildInputs = [ IPCSystemSimple ];
buildInputs = [ FileWhich ];
meta = {
description = "Use the Freedesktop.org base directory specification";
license = with lib.licenses; [ artistic1 gpl1Plus ];
};
};
# fixes: "FAILED IPython/terminal/tests/test_debug_magic.py::test_debug_magic_passes_through_generators - pexpect.exceptions.TIMEOUT: Timeout exceeded."
Testutf8 = buildPerlPackage {
pname = "Test-utf8";
version = "1.02";
src = fetchurl {
url = "mirror://cpan/authors/id/M/MA/MARKF/Test-utf8-1.02.tar.gz";
hash = "sha256-34LwnFlAgwslpJ8cgWL6JNNx5gKIDt742aTUv9Zri9c=";
};
nativeBuildInputs = [ perl ];
meta = {
description = "Handy utf8 tests";
homepage = "https://github.com/2shortplanks/Test-utf8/tree";
license = with lib.licenses; [ artistic1 gpl1Plus ];
};
};
# inherit (pkgs.emulated.perl.pkgs)
# Testutf8
# ;
});
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" prev;
# crossFrom."aarch64-linux" = mkCrossFrom "aarch64-linux" prev;
emulated = mkEmulated prev;
})
# (next: prev:
# let
# emulated = prev.emulated;
# in {
# # packages which don't "cross compile" from x86_64 -> x86_64
# inherit (emulated)
# # aws-crt-cpp # "/build/source/include/aws/crt/Optional.h:6:10: fatal error: utility: No such file or directory"
# # # bash # "configure: error: C compiler cannot create executables"
# # boehmgc # "gc_badalc.cc:29:10: fatal error: new: No such file or directory <new>"
# # c-ares # dns-proto.h:11:10: fatal error: memory: No such file or directory
# # db48 # "./db_cxx.h:59:10: fatal error: iostream.h: No such file or directory"
# # # kexec-tools # "configure: error: C compiler cannot create executables"
# # gmp6 # "configure: error: could not find a working compiler"
# # gtest # "/build/source/googletest/src/gtest_main.cc:30:10: fatal error: cstdio: No such file or directory"
# # icu72 # "../common/unicode/localpointer.h:45:10: fatal error: memory: No such file or directory"
# # # libidn2 # "configure: error: C compiler cannot create executables"
# # ncurses # "configure: error: C compiler cannot create executables"
# ;
# bash = prev.bash.overrideAttrs (orig: {
# # configure doesn't know how to build because it doesn't know where to find crt1.o.
# # some parts of nixpkgs specify the path to it explicitly:
# # - <nixpkgs:pkgs/development/libraries/gcc/libstdc++/5.nix>
# # - <nixpkgs:pkgs/build-support/cc-wrapper/add-flags.sh>
# # alternatively, the wrapper gcc (first item on PATH if we look at a failed bash's env-vars)
# # adds these flags automatically. so we can probably just tell `configure` to *not* use any special gcc other than the wrapper.
# # TESTING IN PROGRESS:
# # - N.B.: BUILDCC is a vlc-ism!
# # BUILDCC = "${prev.stdenv.cc}/bin/${prev.stdenv.cc.targetPrefix}cc"; # has illegal requisites
# CC = "${prev.stdenv.cc}/bin/${prev.stdenv.cc.targetPrefix}cc"; # XXX: tested in nixpkgs: FAILS WITH SAME SIGNATURE. env-vars doesn't show our CC though :-(
# # ^ env vars set here are making their way through, but something else (build script?) is overwriting it
# SANE_CC = "${prev.stdenv.cc}/bin/${prev.stdenv.cc.targetPrefix}cc";
# # CC = "gcc" # bash configure.ac
# # CC_FOR_BUILD = "gcc" # bash configure.ac
# # BUILDCC = "gcc"; # VLC
# });
# }
# )
(nativeSelf: nativeSuper: {
pkgsi686Linux = nativeSuper.pkgsi686Linux.extend (i686Self: i686Super: {
# fixes eval-time error: "Unsupported cross architecture"
# it happens even on a x86_64 -> x86_64 build:
# - defining `config.nixpkgs.buildPlatform` to the non-default causes that setting to be inherited by pkgsi686.
# - hence, `pkgsi686` on a non-cross build is ordinarily *emulated*:
# defining a cross build causes it to also be cross (but to the right hostPlatform)
# this has no inputs other than stdenv, and fetchurl, so emulating it is fine.
tbb = nativeSuper.emulated.pkgsi686Linux.tbb;
# tbb = i686Super.tbb.overrideAttrs (orig: (with i686Self; {
# makeFlags = lib.optionals stdenv.cc.isClang [
# "compiler=clang"
# ] ++ (lib.optional (stdenv.buildPlatform != stdenv.hostPlatform)
# (if stdenv.hostPlatform.isAarch64 then "arch=arm64"
# else if stdenv.hostPlatform.isx86_64 then "arch=intel64"
# else throw "Unsupported cross architecture: ${stdenv.buildPlatform.system} -> ${stdenv.hostPlatform.system}"));
# }));
});
})
(next: prev:
let
emulated = prev.emulated;
# emulated = if prev.stdenv.buildPlatform.system == prev.stdenv.hostPlatform.system then
# prev
# else
# prev.emulated;
in {
# packages which don't cross compile
inherit (emulated)
# adwaita-qt # psqlodbc
apacheHttpd_2_4 # `configure: error: Size of "void *" is less than size of "long"`
# duplicity # python3.10-s3transfer
flatpak # No package 'libxml-2.0' found
fwupd-efi # efi/meson.build:162:0: ERROR: Program or command 'gcc' not found or not executable
# gdk-pixbuf # cross-compiled version doesn't output bin/gdk-pixbuf-thumbnailer (used by webp-pixbuf-loader
gmime3 # "checking preferred charset formats for system iconv... cannot run test program while cross compiling"
# gnome-tour
# XXX: gnustep members aren't individually overridable, because the "scope" uses `rec` such that members don't see overrides
gnustep # gnustep.base: "configure: error: Your compiler does not appear to implement the -fconstant-string-class option needed for support of strings."
# grpc
gvfs # meson.build:312:2: ERROR: Assert failed: http required but libxml-2.0 not found
# hdf5 # configure: error: cannot run test program while cross compiling
# http2
ibus # configure.ac:152: error: possibly undefined macro: AM_PATH_GLIB_2_0
kitty # "FileNotFoundError: [Errno 2] No such file or directory: 'pkg-config'"
libchamplain # "failed to produce output path for output 'devdoc'"
libgccjit # "../../gcc-9.5.0/gcc/jit/jit-result.c:52:3: error: 'dlclose' was not declared in this scope"
libgweather # "Run-time dependency vapigen found: NO (tried pkgconfig)"
libjcat # data/tests/meson.build:10:0: ERROR: Program 'gnutls-certtool certtool' not found or not executable
# libsForQt5 # qtbase # make: g++: No such file or directory
libtiger # "src/tiger_internal.h:24:10: fatal error: pango/pango.h: No such file or directory"
notmuch # "Error: The dependencies of notmuch could not be satisfied" (xapian, gmime, glib, talloc)
# perlInterpreters # perl5.36.0-Module-Build perl5.36.0-Test-utf8 (see tracking issues ^)
phosh # libadwaita-1 not found
# qgnomeplatform
# qtbase
qt5 # qt5.qtx11extras fails, but we can't selectively emulate it
qt6 # "You need to set QT_HOST_PATH to cross compile Qt."
sequoia # "/nix/store/q8hg17w47f9xr014g36rdc2gi8fv02qc-clang-aarch64-unknown-linux-gnu-12.0.1-lib/lib/libclang.so.12: cannot open shared object file: No such file or directory"', /build/sequoia-0.27.0-vendor.tar.gz/bindgen/src/lib.rs:1975:31"
# splatmoji
squeekboard # meson.build:1:0: ERROR: 'rust' compiler binary not defined in cross or native file
twitter-color-emoji # /nix/store/0wk6nr1mryvylf5g5frckjam7g7p9gpi-bash-5.2-p15/bin/bash: line 1: pkg-config: command not found
# unar has multiple failures:
# - "configure: error: Your compiler does not appear to implement the -fconstant-string-class option needed for support of strings. Please check for a more recent version or consider using --enable-nxconstantstring"
# - "/nix/store/0wk6nr1mryvylf5g5frckjam7g7p9gpi-bash-5.2-p15/bin/bash: line 1: ar: command not found"
unar
visidata # python3.10-psycopg2 python3.10-pandas python3.10-h5py
# webkitgtk_4_1 # requires nativeBuildInputs = perl.pkgs.FileCopyRecursive => perl5.36.0-Test-utf8
# xdg-utils # perl5.36.0-File-BaseDir / perl5.36.0-Module-Build
;
# apacheHttpd_2_4 = prev.apacheHttpd_2_4.override {
# # fixes original error
# # new failure mode: "/nix/store/czvaa9y9ch56z53c0b0f5bsjlgh14ra6-apr-aarch64-unknown-linux-gnu-1.7.0-dev/share/build/libtool: line 1890: aarch64-unknown-linux-gnu-ar: command not found"
# inherit (emulated) stdenv;
# };
# mod_dnssd = prev.mod_dnssd.override {
# inherit (emulated) stdenv;
# };
apacheHttpdPackagesFor = apacheHttpd: self:
let
prevHttpdPkgs = prev.apacheHttpdPackagesFor apacheHttpd self;
in prevHttpdPkgs // {
# fixes "configure: error: *** Sorry, could not find apxs ***"
mod_dnssd = prevHttpdPkgs.mod_dnssd.override {
inherit (emulated) stdenv;
};
};
# apacheHttpdPackagesFor = apacheHttpd: self:
# let
# prevHttpdPkgs = lib.fix (emulated.apacheHttpdPackagesFor apacheHttpd);
# in
# (prev.apacheHttpdPackagesFor apacheHttpd self) // {
# # inherit (prevHttpdPkgs) mod_dnssd;
# mod_dnssd = prevHttpdPkgs.mod_dnssd.override {
# inherit (self) apacheHttpd;
# };
# };
# appstream = prev.appstream.override {
# # doesn't fix: "ld: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
# appstream = prev.appstream.overrideAttrs (orig: {
# # fixes "Program 'gperf' not found or not executable"
# # does not fix "ERROR: An exe_wrapper is needed but was not found. Please define one in cross file and check the command and/or add it to PATH."
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gperf ];
# });
# appstream = prev.appstream.overrideAttrs (upstream: {
# # does not fix "Program 'gperf' not found or not executable"
# nativeBuildInputs = upstream.nativeBuildInputs ++ lib.optionals (!prev.stdenv.buildPlatform.canExecute prev.stdenv.hostPlatform) [
# next.mesonEmulatorHook
# ];
# });
appstream = prev.appstream.overrideAttrs (upstream: {
# fixes "Program 'gperf' not found or not executable"
nativeBuildInputs = upstream.nativeBuildInputs ++ lib.optionals (!prev.stdenv.buildPlatform.canExecute prev.stdenv.hostPlatform) [
next.mesonEmulatorHook
] ++ [
next.gperf
];
});
blueman = prev.blueman.overrideAttrs (orig: {
# configure: error: ifconfig or ip not found, install net-tools or iproute2
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.iproute2 ];
});
brltty = prev.brltty.override {
# configure: error: no acceptable C compiler found in $PATH
inherit (emulated) stdenv;
};
browserpass = prev.browserpass.override {
# fixes "qemu-aarch64: Could not open '/lib/ld-linux-aarch64.so.1': No such file or directory"
inherit (emulated) buildGoModule; # buildGoModule holds the stdenv
};
cantarell-fonts = prev.cantarell-fonts.override {
# fixes error where python3.10-skia-pathops dependency isn't available for the build platform
inherit (emulated) stdenv;
};
cdrtools = prev.cdrtools.override {
# "configure: error: installation or configuration problem: C compiler cc not found."
inherit (emulated) stdenv;
};
# colord = prev.colord.override {
# # doesn't fix: "ld: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
colord = prev.colord.overrideAttrs (upstream: {
# fixes: (meson) ERROR: An exe_wrapper is needed but was not found. Please define one in cross file and check the command and/or add it to PATH.
nativeBuildInputs = upstream.nativeBuildInputs ++ lib.optionals (!prev.stdenv.buildPlatform.canExecute prev.stdenv.hostPlatform) [
next.mesonEmulatorHook
];
});
dante = prev.dante.override {
# fixes: "configure: error: error: getaddrinfo() error value count too low"
inherit (emulated) stdenv;
};
emacs = prev.emacs.override {
# fixes "configure: error: cannot run test program while cross compiling"
inherit (emulated) stdenv;
};
# flatpak = prev.flatpak.override {
# # doesn't fix: "ld: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
fuzzel = prev.fuzzel.override {
# meson.build:100:0: ERROR: Dependency lookup for wayland-scanner with method 'pkgconfig' failed: Pkg-config binary for machine 0 not found. Giving up.
inherit (emulated) stdenv;
};
# fwupd-efi = prev.fwupd-efi.override {
# # efi/meson.build:33:2: ERROR: Problem encountered: gnu-efi support requested, but headers were not found
# inherit (emulated) stdenv;
# };
fwupd = prev.fwupd.overrideAttrs (orig: {
# solves (meson) "Run-time dependency libgcab-1.0 found: NO (tried pkgconfig and cmake)", and others.
buildInputs = orig.buildInputs ++ [ next.gcab next.gnutls ];
mesonFlags = (lib.remove "-Ddocs=enabled" orig.mesonFlags) ++ [ "-Ddocs=disabled" ];
outputs = lib.remove "devdoc" orig.outputs;
});
# fwupd = prev.fwupd.override {
# # solves missing libgcab-1.0;
# # new error: "meson.build:449:4: ERROR: Command "/nix/store/n7xrj3pnrgcr8igx7lfhz8197y67bk7k-python3-aarch64-unknown-linux-gnu-3.10.9-env/bin/python3 po/test-deps" failed with status 1."
# inherit (emulated) stdenv;
# };
gcr_4 = prev.gcr_4.overrideAttrs (orig: {
# fixes (meson): "ERROR: Program 'gpg2 gpg' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gnupg next.openssh ];
});
gthumb = prev.gthumb.overrideAttrs (orig: {
# fixes (meson) "Program 'glib-mkenums mkenums' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
gmime = prev.gmime.overrideAttrs (orig: {
# "checking preferred charset formats for system iconv... cannot run test program while cross compiling"
configureFlags = orig.configureFlags ++ [ "ac_cv_have_iconv_detect_h=no" ];
});
# gmime3 = prev.gmime3.overrideAttrs (orig: {
# # "checking preferred charset formats for system iconv... cannot run test program while cross compiling"
# # unsolved: "ImportError: /nix/store/c190src4bjkfp7bdgc5sadnmvgzv7kxb-gobject-introspection-aarch64-unknown-linux-gnu-1.74.0/lib/gobject-introspection/giscanner/_giscanner.cpython-310-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory"
# configureFlags = orig.configureFlags ++ [ "ac_cv_have_iconv_detect_h=no" ];
# });
# gmime3 = prev.gmime3.override {
# # doesn't fix
# inherit (emulated) stdenv;
# };
gnome = prev.gnome.overrideScope' (self: super: {
inherit (emulated.gnome)
dconf-editor # "error: Package `dconf' not found in specified Vala API directories or GObject-Introspection GIR directories"
evolution-data-server # "The 'perl' not found, not installing csv2vcard"
gnome-shell # "meson.build:128:0: ERROR: Program 'gjs' not found or not executable"
gnome-settings-daemon # subprojects/gvc/meson.build:30:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable
mutter # meson.build:237:2: ERROR: Dependency "gbm" not found, tried pkgconfig (it's provided by mesa)
;
# dconf-editor = super.dconf-editor.override {
# # fails to fix original error
# inherit (emulated) stdenv;
# };
# dconf-editor = super.dconf-editor.overrideAttrs (orig: {
# # fails to fix original error
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.dconf ];
# });
evince = super.evince.overrideAttrs (orig: {
# fixes (meson) "Run-time dependency gi-docgen found: NO (tried pkgconfig and cmake)"
# inspired by gupnp
outputs = [ "out" "dev" ]
++ lib.optionals (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform) [ "devdoc" ];
mesonFlags = orig.mesonFlags ++ [
"-Dgtk_doc=${lib.boolToString (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform)}"
];
});
# evolution-data-server = super.evolution-data-server.override {
# inherit (next) stdenv;
# };
# evolution-data-server = super.evolution-data-server.overrideAttrs (orig: {
# # fixes "The 'perl' not found, not installing csv2vcard"
# # doesn't fix "CMake Error: try_run() invoked in cross-compiling mode, please set the following cache variables appropriately"
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.perl ];
# });
file-roller = super.file-roller.override {
# fixes "src/meson.build:106:0: ERROR: Program 'glib-compile-resources' not found or not executable"
inherit (emulated) stdenv;
};
gnome-color-manager = super.gnome-color-manager.overrideAttrs (orig: {
# fixes: "src/meson.build:3:0: ERROR: Program 'glib-compile-resources' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
gnome-control-center = super.gnome-control-center.overrideAttrs (orig: {
# fixes "subprojects/gvc/meson.build:30:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
# gnome-control-center = super.gnome-control-center.override {
# inherit (next) stdenv;
# };
# gnome-keyring = super.gnome-keyring.override {
# # does not fix original error
# inherit (next) stdenv;
# };
gnome-keyring = super.gnome-keyring.overrideAttrs (orig: {
# fixes "configure.ac:374: error: possibly undefined macro: AM_PATH_LIBGCRYPT"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.libgcrypt next.openssh next.glib ];
});
gnome-remote-desktop = super.gnome-remote-desktop.overrideAttrs (orig: {
# TODO: remove gnome-remote-desktop (wanted by gnome-control-center)
# fixes: "Program gdbus-codegen found: NO"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
# gnome-shell = super.gnome-shell.overrideAttrs (orig: {
# # fixes "meson.build:128:0: ERROR: Program 'gjs' not found or not executable"
# # does not fix "_giscanner.cpython-310-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory" (python import failure)
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gjs next.gobject-introspection ];
# # try to reduce gobject-introspection/shew dependencies
# mesonFlags = [
# "-Dextensions_app=false"
# "-Dextensions_tool=false"
# "-Dman=false"
# ];
# # fixes "gvc| Build-time dependency gobject-introspection-1.0 found: NO"
# # inspired by gupnp_1_6
# # outputs = [ "out" "dev" ]
# # ++ lib.optionals (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform) [ "devdoc" ];
# # mesonFlags = [
# # "-Dgtk_doc=${lib.boolToString (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform)}"
# # ];
# });
# gnome-shell = super.gnome-shell.override {
# inherit (next) stdenv;
# };
# gnome-shell = super.gnome-shell.overrideAttrs (orig: {
# # does not solve original error
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.mesonEmulatorHook ];
# });
# gnome-settings-daemon = super.gnome-settings-daemon.overrideAttrs (orig: {
# # fixes "subprojects/gvc/meson.build:30:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
# # new error: "plugins/power/meson.build:22:0: ERROR: Dependency lookup for glib-2.0 with method 'pkgconfig' failed: Pkg-config binary for machine 0 not found. Giving up."
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
# });
# gnome-settings-daemon = super.gnome-settings-daemon.overrideAttrs (orig: {
# # does not fix original error
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.mesonEmulatorHook ];
# });
gnome-session = super.gnome-session.overrideAttrs (orig: {
# fixes: "gdbus-codegen not found or executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
# gnome-terminal = super.gnome-terminal.override {
# # fixes: "meson.build:343:0: ERROR: Dependency "libpcre2-8" not found, tried pkgconfig"
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libglib-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
gnome-terminal = super.gnome-terminal.overrideAttrs (orig: {
# fixes "meson.build:343:0: ERROR: Dependency "libpcre2-8" not found, tried pkgconfig"
buildInputs = orig.buildInputs ++ [ next.pcre2 ];
});
gnome-user-share = super.gnome-user-share.overrideAttrs (orig: {
# fixes: meson.build:111:6: ERROR: Program 'glib-compile-schemas' not found or not executable
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
# mutter = super.mutter.override {
# # DOES NOT FIX: "meson.build:237:2: ERROR: Dependency "gbm" not found, tried pkgconfig (it's provided by mesa)"
# inherit (next) stdenv;
# };
# mutter = super.mutter.overrideAttrs (orig: {
# # fixes "meson.build:237:2: ERROR: Dependency "gbm" not found, tried pkgconfig (it's provided by mesa)"
# # new error: "/nix/store/c190src4bjkfp7bdgc5sadnmvgzv7kxb-gobject-introspection-aarch64-unknown-linux-gnu-1.74.0/lib/gobject-introspection/giscanner/_giscanner.cpython-310-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory"
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gobject-introspection next.wayland-scanner ];
# buildInputs = orig.buildInputs ++ [ next.mesa ];
# # disable docs building
# mesonFlags = lib.remove "-Ddocs=true" orig.mesonFlags;
# });
# mutter = super.mutter.overrideAttrs (orig: {
# # TODO: something seems to be propagating an *emulated* version of gobject-introspection into the build
# nativeBuildInputs =
# (lib.remove next.python3
# (lib.remove next.mesa orig.nativeBuildInputs)
# )
# ++ [
# next.gobject-introspection
# next.mesonEmulatorHook
# next.python3.pythonForBuild
# next.wayland-scanner
# ];
# buildInputs = (lib.remove next.gobject-introspection orig.buildInputs)
# ++ [ next.mesa ];
# # disable docs building
# mesonFlags = lib.remove "-Ddocs=true" orig.mesonFlags;
# });
# nautilus = super.nautilus.override {
# # fixes: "meson.build:123:0: ERROR: Dependency "libxml-2.0" not found, tried pkgconfig"
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libglib-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
nautilus = super.nautilus.overrideAttrs (orig: {
# fixes: "meson.build:123:0: ERROR: Dependency "libxml-2.0" not found, tried pkgconfig"
buildInputs = orig.buildInputs ++ [ next.libxml2 ];
});
});
gocryptfs = prev.gocryptfs.override {
# fixes "error: hash mismatch in fixed-output derivation" (vendorSha256)
inherit (emulated) buildGoModule; # equivalent to stdenv
};
gupnp_1_6 = prev.gupnp_1_6.overrideAttrs (orig: {
# fixes "subprojects/gi-docgen/meson.build:10:0: ERROR: python3 not found"
# this patch is copied from the default gupnp.
# TODO: upstream
outputs = [ "out" "dev" ]
++ lib.optionals (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform) [ "devdoc" ];
mesonFlags = [
"-Dgtk_doc=${lib.boolToString (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform)}"
"-Dintrospection=${lib.boolToString (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform)}"
];
});
gst_all_1 = prev.gst_all_1 // {
# gst-editing-services = prev.gst_all_1.gst-editing-services.override {
# # fixes "Run-time dependency gst-validate-1.0 found: NO"
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libgobject-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
# XXX this feels risky; it propagates a (conflicting) gst-plugins to all consumers
# gst-editing-services = emulated.gst_all_1.gst-editing-services;
gst-editing-services = prev.gst_all_1.gst-editing-services.overrideAttrs (orig: {
# fixes "Run-time dependency gst-validate-1.0 found: NO"
buildInputs = orig.buildInputs ++ [ next.gst_all_1.gst-devtools ];
mesonFlags = orig.mesonFlags ++ [
# disable "python formatters" to avoid undefined references to Py_Initialize, etc.
"-Dpython=disabled"
];
});
inherit (emulated.gst_all_1) gst-plugins-good;
# gst-plugins-good = prev.gst_all_1.gst-plugins-good.override {
# # when invoked with `qt5Support = true`, qtbase shows up in both buildInputs and nativeBuildInputs
# # if these aren't identical, then qt complains: "Error: detected mismatched Qt dependencies"
# # doesn't fix the original error.
# inherit (emulated) stdenv;
# # qt5Support = true;
# };
};
# gvfs = prev.gvfs.overrideAttrs (orig: {
# # meson.build:312:2: ERROR: Assert failed: http required but libxml-2.0 not found
# # nativeBuildInputs = orig.nativeBuildInputs ++ [ prev.libxml2 prev.mesonEmulatorHook ];
# # TODO: gvfs 1.50.2 -> 1.50.3 upgrade is upstreamed, and fixed cross compilation
# version = "1.50.3";
# src = next.fetchurl {
# url = "mirror://gnome/sources/gvfs/1.50/gvfs-1.50.3.tar.xz";
# sha256 = "aJcRnpe7FgKdJ3jhpaVKamWSYx+LLzoqHepO8rAYA/0=";
# };
# patches = [
# # Hardcode the ssh path again.
# # https://gitlab.gnome.org/GNOME/gvfs/-/issues/465
# (next.fetchpatch2 {
# url = "https://gitlab.gnome.org/GNOME/gvfs/-/commit/8327383e262e1e7f32750a8a2d3dd708195b0f53.patch";
# hash = "sha256-ReD7qkezGeiJHyo9jTqEQNBjECqGhV9nSD+dYYGZWJ8=";
# revert = true;
# })
# ];
# });
# gvfs = prev.gvfs.override {
# # solves original config-time problem
# # new failure: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/3n0n1s5gb34lkckkr8ix5b7s5hz4n48v-libxml2-2.10.3/lib/libxml2.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
hdf5 = prev.hdf5.override {
inherit (emulated) stdenv;
};
# ibus = prev.ibus.override {
# # "_giscanner.cpython-310-x86_64-linux-gnu.so: cannot open shared object file: No such file or directory"
# inherit (emulated) stdenv;
# };
iio-sensor-proxy = prev.iio-sensor-proxy.overrideAttrs (orig: {
# fixes "./autogen.sh: line 26: gtkdocize: not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib next.gtk-doc ];
});
# kitty = prev.kitty.override {
# # does not solve original error
# inherit (emulated) stdenv;
# };
# libchamplain = prev.libchamplain.override {
# # fails: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libglib-2.0.so: error adding symbols: file in wrong format";
# inherit (emulated) stdenv;
# };
# libgweather = prev.libgweather.override {
# # solves original problem
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libgio-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
libHX = prev.libHX.overrideAttrs (orig: {
# "Can't exec "libtoolize": No such file or directory at /nix/store/r4fvx9hazsm0rdm7s393zd5v665dsh1c-autoconf-2.71/share/autoconf/Autom4te/FileUtils.pm line 294."
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.libtool ];
});
# libjcat = prev.libjcat.override {
# # fixes original error
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libgio-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
librest = prev.librest.overrideAttrs (orig: {
# fixes "You must have gtk-doc >= 1.13 installed to build documentation"
# by removing the "--enable-gtk-doc" flag
configureFlags = [ "--with-ca-certificates=/etc/ssl/certs/ca-certificates.crt" ];
});
librest_1_0 = prev.librest_1_0.overrideAttrs (orig: {
# fixes (meson) "Run-time dependency gi-docgen found: NO (tried pkgconfig and cmake)"
# inspired by gupnp
outputs = [ "out" "dev" ]
++ lib.optionals (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform) [ "devdoc" ];
mesonFlags = orig.mesonFlags ++ [
"-Dgtk_doc=${lib.boolToString (prev.stdenv.buildPlatform == prev.stdenv.hostPlatform)}"
];
});
libsForQt5 = prev.libsForQt5.overrideScope' (self: super: {
qgpgme = super.qgpgme.overrideAttrs (orig: {
# fix so it can find the MOC compiler
# it looks like it might not *need* to propagate qtbase, but so far unclear
nativeBuildInputs = orig.nativeBuildInputs ++ [ self.qtbase ];
propagatedBuildInputs = lib.remove self.qtbase orig.propagatedBuildInputs;
});
phonon = super.phonon.overrideAttrs (orig: {
# fixes "ECM (required version >= 5.60), Extra CMake Modules"
buildInputs = orig.buildInputs ++ [ next.extra-cmake-modules ];
});
});
# libtiger = prev.libtiger.override {
# # fails to fix: "src/tiger_internal.h:24:10: fatal error: pango/pango.h: No such file or directory"
# inherit (emulated) stdenv;
# };
# libtiger = prev.libtiger.overrideAttrs (orig: {
# # fails to fix: "src/tiger_internal.h:24:10: fatal error: pango/pango.h: No such file or directory"
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.libkate next.cairo next.pango ];
# });
ncftp = prev.ncftp.override {
# fixes: "ar: No such file or directory"
inherit (emulated) stdenv;
};
networkmanager-fortisslvpn = prev.networkmanager-fortisslvpn.overrideAttrs (orig: {
# fixes "gdbus-codegen: command not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
# networkmanager-iodine = prev.networkmanager-iodine.overrideAttrs (orig: {
# # fails to fix "configure.ac:58: error: possibly undefined macro: AM_GLIB_GNU_GETTEXT"
# nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gettext ];
# });
networkmanager-iodine = prev.networkmanager-iodine.override {
# fixes "configure.ac:58: error: possibly undefined macro: AM_GLIB_GNU_GETTEXT"
inherit (emulated) stdenv;
};
networkmanager-l2tp = prev.networkmanager-l2tp.overrideAttrs (orig: {
# fixes "gdbus-codegen: command not found"
# fixes "gtk4-builder-tool: command not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib next.gtk4 ];
});
networkmanager-openconnect = prev.networkmanager-openconnect.overrideAttrs (orig: {
# fixes "properties/gresource.xml: Permission denied"
# - by providing glib-compile-resources
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
networkmanager-openvpn = prev.networkmanager-openvpn.overrideAttrs (orig: {
# fixes "properties/gresource.xml: Permission denied"
# - by providing glib-compile-resources
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
networkmanager-sstp = prev.networkmanager-sstp.overrideAttrs (orig: {
# fixes "gdbus-codegen: command not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
networkmanager-vpnc = prev.networkmanager-vpnc.overrideAttrs (orig: {
# fixes "properties/gresource.xml: Permission denied"
# - by providing glib-compile-resources
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
nheko = prev.nheko.overrideAttrs (orig: {
# fixes "fatal error: lmdb++.h: No such file or directory
buildInputs = orig.buildInputs ++ [ next.lmdbxx ];
});
# notmuch = prev.notmuch.override {
# # fails to solve original error
# inherit (emulated) stdenv;
# };
obex_data_server = prev.obex_data_server.override {
# fixes "/nix/store/0wk6nr1mryvylf5g5frckjam7g7p9gpi-bash-5.2-p15/bin/bash: line 2: --prefix=ods_manager: command not found"
inherit (emulated) stdenv;
};
openfortivpn = prev.openfortivpn.override {
# fixes "checking for /proc/net/route... configure: error: cannot check for file existence when cross compiling"
inherit (emulated) stdenv;
};
ostree = prev.ostree.override {
# fixes "configure: error: Need GPGME_PTHREAD version 1.1.8 or later"
inherit (emulated) stdenv;
};
pam_mount = prev.pam_mount.overrideAttrs (orig: {
# fixes: "perl: command not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.perl ];
});
phoc = prev.phoc.override {
# fixes "Program wayland-scanner found: NO"
inherit (emulated) stdenv;
};
# phosh = prev.phosh.override {
# # fixes original error.
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/2bzd39fbsifidd667s7x930d0b7pm3qx-pango-1.50.12/lib/libpangocairo-1.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
phosh-mobile-settings = prev.phosh-mobile-settings.override {
# fixes "meson.build:26:0: ERROR: Dependency "phosh-plugins" not found, tried pkgconfig"
inherit (emulated) stdenv;
};
pipewire = prev.pipewire.overrideAttrs (orig: {
# fix `spa/plugins/bluez5/meson.build:41:0: ERROR: Program 'gdbus-codegen' not found or not executable`
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
psqlodbc = prev.psqlodbc.override {
# fixes "configure: error: odbc_config not found (required for unixODBC build)"
inherit (emulated) stdenv;
};
pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
(py-next: py-prev: {
defcon = py-prev.defcon.overridePythonAttrs (orig: {
# TODO: diagnose and upstream
nativeBuildInputs = orig.nativeBuildInputs ++ orig.nativeCheckInputs;
});
executing = py-prev.executing.overridePythonAttrs (orig: {
# TODO: confirm & upstream
# test has an assertion that < 1s of CPU time elapsed => flakey
disabledTestPaths = orig.disabledTestPaths or [] ++ [
# "tests/test_main.py::TestStuff::test_many_source_for_filename_calls"
"tests/test_main.py"
];
});
# h5py = py-prev.h5py.overridePythonAttrs (orig: {
# # XXX: can't upstream until its dependency, hdf5, is fixed. that looks TRICKY.
# # - the `setup_configure.py` in h5py tries to dlopen (and call into) the hdf5 lib to query the version and detect features like MPI
# # - it could be patched with ~10 LoC in the HDF5LibWrapper class.
# #
# # expose numpy and hdf5 as available at build time
# nativeBuildInputs = orig.nativeBuildInputs ++ orig.propagatedBuildInputs ++ orig.buildInputs;
# buildInputs = [];
# # HDF5_DIR = "${hdf5}";
# });
ipython = py-prev.ipython.overridePythonAttrs (orig: {
# fixes "FAILED IPython/terminal/tests/test_debug_magic.py::test_debug_magic_passes_through_generators - pexpect.exceptions.TIMEOUT: Timeout exceeded."
disabledTests = orig.disabledTests ++ [ "test_debug_magic_passes_through_generator" ];
});
mutatormath = py-prev.mutatormath.overridePythonAttrs (orig: {
# TODO: diagnose and upstream
nativeBuildInputs = orig.nativeBuildInputs or [] ++ orig.nativeCheckInputs;
});
pandas = py-prev.pandas.overridePythonAttrs (orig: {
# TODO: upstream
# XXX: we only actually need numpy when building in ~/nixpkgs repo: not sure why we need all the propagatedBuildInputs here.
# nativeBuildInputs = orig.nativeBuildInputs ++ [ py-next.numpy ];
nativeBuildInputs = orig.nativeBuildInputs ++ orig.propagatedBuildInputs;
});
psycopg2 = py-prev.psycopg2.overridePythonAttrs (orig: {
# TODO: upstream (see tracking issue)
#
# psycopg2 *links* against libpg, so we need the host postgres available at build time!
# present-day nixpkgs only includes it in nativeBuildInputs
buildInputs = orig.buildInputs ++ [ next.postgresql ];
});
s3transfer = py-prev.s3transfer.overridePythonAttrs (orig: {
# tests explicitly expect host CPU == build CPU
# Bail out! ERROR:../plugins/core.c:221:qemu_plugin_vcpu_init_hook: assertion failed: (success)
# Bail out! ERROR:../accel/tcg/cpu-exec.c:954:cpu_exec: assertion failed: (cpu == current_cpu)
disabledTestPaths = orig.disabledTestPaths ++ [
# "tests/functional/test_processpool.py::TestProcessPoolDownloader::test_cleans_up_tempfile_on_failure"
"tests/functional/test_processpool.py"
# "tests/unit/test_compat.py::TestBaseManager::test_can_provide_signal_handler_initializers_to_start"
"tests/unit/test_compat.py"
];
});
# skia-pathops = ?
# it tries to call `cc` during the build, but can't find it.
})
];
# qt5 = prev.qt5.overrideScope' (self: super: {
# qtbase = super.qtbase.override {
# inherit (emulated) stdenv;
# };
# qtx11extras = super.qtx11extras.override {
# # "Project ERROR: Cannot run compiler 'g++'";
# # this fails an assert though, where the cross qt now references the emulated qt.
# inherit (emulated.qt5) qtModule;
# };
# });
# qt6 = prev.qt6.overrideScope' (self: super: {
# qtbase = super.qtbase.override {
# # fixes: "You need to set QT_HOST_PATH to cross compile Qt."
# inherit (emulated) stdenv;
# };
# });
rapidfuzz-cpp = prev.rapidfuzz-cpp.overrideAttrs (orig: {
# fixes "error: could not find git for clone of catch2-populate"
buildInputs = orig.buildInputs or [] ++ [ next.catch2_3 ];
});
re2 = (prev.re2.override {
# fixes: "FAILED: CMakeFiles/test.util"
inherit (emulated) stdenv;
}).overrideAttrs (orig: {
# exhaustive{,1,2}_test times out after 1500s.
# this is after exhaustive3_test takes 600s to pass.
doCheck = false;
});
rmlint = prev.rmlint.override {
# fixes "Checking whether the C compiler works... no"
inherit (emulated) stdenv;
};
# sequoia = prev.sequoia.override {
# # fails to fix original error
# inherit (emulated) stdenv;
# };
# squeekboard = prev.squeekboard.overrideAttrs (orig: {
# # fixes: "meson.build:1:0: ERROR: 'rust' compiler binary not defined in cross or native file"
# # new error: "meson.build:1:0: ERROR: Rust compiler rustc --target aarch64-unknown-linux-gnu -C linker=aarch64-unknown-linux-gnu-gcc can not compile programs."
# mesonFlags =
# let
# # ERROR: 'rust' compiler binary not defined in cross or native file
# crossFile = next.writeText "cross-file.conf" ''
# [binaries]
# rust = [ 'rustc', '--target', '${next.rust.toRustTargetSpec next.stdenv.hostPlatform}' ]
# '';
# in
# orig.mesonFlags or [] ++ lib.optionals (next.stdenv.hostPlatform != next.stdenv.buildPlatform) [ "--cross-file=${crossFile}" ];
# });
# squeekboard = prev.squeekboard.override {
# # new error: "gcc: error: unrecognized command line option '-m64'"
# inherit (emulated) stdenv;
# };
sysprof = prev.sysprof.overrideAttrs (orig: {
# fixes: "src/meson.build:12:2: ERROR: Program 'gdbus-codegen' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
tpm2-abrmd = prev.tpm2-abrmd.overrideAttrs (orig: {
# fixes "configure: error: *** gdbus-codegen is required to build tpm2-abrmd; No package 'gio-unix-2.0' found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.glib ];
});
tracker-miners = prev.tracker-miners.override {
# fixes "meson.build:183:0: ERROR: Can not run test applications in this cross environment."
inherit (emulated) stdenv;
};
# twitter-color-emoji = prev.twitter-color-emoji.override {
# # fails to fix original error
# inherit (emulated) stdenv;
# };
# unar = (prev.unar.override {
# # fixes "ar: command not found"
# # new error: "gcc: error: unrecognized command line option '-fobjc-runtime=gnustep-2.0'"
# inherit (emulated) stdenv;
# });
unixODBCDrivers = prev.unixODBCDrivers // {
# TODO: should this package be deduped with toplevel psqlodbc in upstream nixpkgs?
psql = prev.unixODBCDrivers.psql.override {
# fixes "configure: error: odbc_config not found (required for unixODBC build)"
inherit (emulated) stdenv;
};
# psql = prev.unixODBCDrivers.psql.overrideAttrs (orig: {
# # fixes "configure: error: odbc_config not found (required for unixODBC build)"
# # new error: "/nix/store/h3ms3h95rbj5p8yhxfhbsbnxgvpnb8w0-aarch64-unknown-linux-gnu-binutils-2.39/bin/aarch64-unknown-linux-gnu-ld: /nix/store/6h6z98qvg5k8rsqpivi42r5008zjfp2v-unixODBC-2.3.11/lib/libodbcinst.so: error adding symbols: file in wrong format"
# nativeBuildInputs = orig.nativeBuildInputs or [] ++ orig.buildInputs;
# });
};
vlc = prev.vlc.overrideAttrs (orig: {
# fixes: "configure: error: could not find the LUA byte compiler"
# fixes: "configure: error: protoc compiler needed for chromecast was not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.lua5 next.protobuf ];
# fix that it can't find the c compiler
# makeFlags = orig.makeFlags or [] ++ [ "CC=${prev.stdenv.cc.targetPrefix}cc" ];
BUILDCC = "${prev.stdenv.cc}/bin/${prev.stdenv.cc.targetPrefix}cc";
});
vpnc = prev.vpnc.overrideAttrs (orig: {
# fixes "perl: command not found"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.perl ];
});
xdg-desktop-portal-gtk = prev.xdg-desktop-portal-gtk.overrideAttrs (orig: {
# fixes "No package 'xdg-desktop-portal' found"
buildInputs = orig.buildInputs ++ [ next.xdg-desktop-portal ];
});
xdg-desktop-portal-gnome = prev.xdg-desktop-portal-gnome.overrideAttrs (orig: {
# fixes: "data/meson.build:33:5: ERROR: Program 'msgfmt' not found or not executable"
# fixes: "src/meson.build:25:0: ERROR: Program 'gdbus-codegen' not found or not executable"
nativeBuildInputs = orig.nativeBuildInputs ++ [ next.gettext next.glib ];
});
# webp-pixbuf-loader = prev.webp-pixbuf-loader.override {
# # fixes "Builder called die: Cannot wrap '/nix/store/kpp8qhzdjqgvw73llka5gpnsj0l4jlg8-gdk-pixbuf-aarch64-unknown-linux-gnu-2.42.10/bin/gdk-pixbuf-thumbnailer' because it is not an executable file"
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/2syg6jxk8zi1zkpqvkxkz87x8sl27c6b-gdk-pixbuf-2.42.10/lib/libgdk_pixbuf-2.0.so: error adding symbols: file in wrong format"
# inherit (emulated) stdenv;
# };
webp-pixbuf-loader = prev.webp-pixbuf-loader.overrideAttrs (upstream: {
# fixes: "Builder called die: Cannot wrap '/nix/store/kpp8qhzdjqgvw73llka5gpnsj0l4jlg8-gdk-pixbuf-aarch64-unknown-linux-gnu-2.42.10/bin/gdk-pixbuf-thumbnailer' because it is not an executable file"
# gdk-pixbuf doesn't create a `bin/` directory when cross-compiling, breaks some thumbnailing stuff.
# see `librsvg` for a more bullet-proof cross-compilation approach
postInstall = "";
});
})
];
};
}

View File

@@ -1,87 +1,74 @@
{ lib, pkgs, ... }:
{ pkgs, ... }:
{
imports = [
./cross.nix
./feeds.nix
./fs.nix
./hardware
./hardware.nix
./home
./hosts.nix
./i2p.nix
./ids.nix
./machine-id.nix
./net.nix
./nix-path
./persist.nix
./programs
./programs.nix
./secrets.nix
./ssh.nix
./users
./users.nix
./vpn.nix
];
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"
];
services.journald.extraConfig = ''
# docs: `man journald.conf`
# merged journald config is deployed to /etc/systemd/journald.conf
[Journal]
# disable journal compression because the underlying fs is compressed
Compress=no
'';
systemd.services.nix-daemon.serviceConfig = {
# the nix-daemon manages nix builders
# kill nix-daemon subprocesses when systemd-oomd detects an out-of-memory condition
# see:
# - nixos PR that enabled systemd-oomd: <https://github.com/NixOS/nixpkgs/pull/169613>
# - systemd's docs on these properties: <https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#ManagedOOMSwap=auto%7Ckill>
#
# systemd's docs warn that without swap, systemd-oomd might not be able to react quick enough to save the system.
# see `man oomd.conf` for further tunables that may help.
#
# alternatively, apply this more broadly with `systemd.oomd.enableSystemSlice = true` or `enableRootSlice`
# TODO: also apply this to the guest user's slice (user-1100.slice)
# TODO: also apply this to distccd
ManagedOOMMemoryPressure = "kill";
ManagedOOMSwap = "kill";
};
system.activationScripts.nixClosureDiff = {
supportsDryActivation = true;
text = ''
# show which packages changed versions or are new/removed in this upgrade
# source: <https://github.com/luishfonseca/dotfiles/blob/32c10e775d9ec7cc55e44592a060c1c9aadf113e/modules/upgrade-diff.nix>
${pkgs.nvd}/bin/nvd --nix-bin-dir=${pkgs.nix}/bin diff /run/current-system "$systemConfig"
'';
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 = [];
# programs.vim.defaultEditor = true;
environment.variables = {
EDITOR = "vim";
# git claims it should use EDITOR, but it doesn't!
GIT_EDITOR = "vim";
# TODO: these should be moved to `home.sessionVariables` (home-manager)
# Electron apps should use native wayland backend:
# https://nixos.wiki/wiki/Slack#Wayland
# Discord under sway crashes with this.
# NIXOS_OZONE_WL = "1";
# LIBGL_ALWAYS_SOFTWARE = "1";
};
# dconf docs: <https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/desktop_migration_and_administration_guide/profiles>
# this lets programs temporarily write user-level dconf settings (aka gsettings).
# they're written to ~/.config/dconf/user, unless `DCONF_PROFILE` is set to something other than the default of /etc/dconf/profile/user
# find keys/values with `dconf dump /`
programs.dconf.enable = true;
programs.dconf.packages = [
@@ -94,7 +81,6 @@
'';
})
];
# sane.programs.glib.enableFor.user.colin = true; # for `gsettings`
# link debug symbols into /run/current-system/sw/lib/debug
# hopefully picked up by gdb automatically?

View File

@@ -1,13 +1,3 @@
# where to find good stuff?
# - podcasts w/ a community: <https://lemmyverse.net/communities?query=podcast>
# - podcast rec thread: <https://lemmy.ml/post/1565858>
#
# 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"; };
@@ -57,76 +47,63 @@ let
};
podcasts = [
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech) # ACQ2 - more "Acquired" episodes
(fromDb "allinchamathjason.libsyn.com" // pol)
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech) # Civboot -- https://anchor.fm/civboot
(fromDb "cast.postmarketos.org" // tech)
(fromDb "congressionaldish.libsyn.com" // pol) # Jennifer Briney
(fromDb "craphound.com" // pol) # Cory Doctorow -- both podcast & text entries
(fromDb "darknetdiaries.com" // tech)
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech) # Matrix (chat) Live
(fromDb "feeds.99percentinvisible.org/99percentinvisible" // pol) # 99% Invisible -- also available here: <https://feeds.simplecast.com/BqbsxVfO>
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
(fromDb "feeds.feedburner.com/dancarlin/history" // rat)
(fromDb "feeds.feedburner.com/radiolab" // pol) # Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
(fromDb "feeds.libsyn.com/421877" // rat) # Less Wrong Curated
(fromDb "feeds.megaphone.fm/behindthebastards" // pol) # also Maggie Killjoy
(fromDb "feeds.megaphone.fm/hubermanlab" // uncat) # Daniel Huberman on sleep
(fromDb "feeds.megaphone.fm/recodedecode" // tech) # The Verge - Decoder
(fromDb "feeds.simplecast.com/54nAGcIl" // pol) # The Daily
(fromDb "feeds.simplecast.com/82FI35Px" // pol) # Ezra Klein Show
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat) # Econ Talk
(fromDb "feeds.simplecast.com/xKJ93w_w" // uncat) # Atlas Obscura
(fromDb "feeds.transistor.fm/acquired" // tech)
(fromDb "lexfridman.com/podcast" // rat)
(fromDb "mapspodcast.libsyn.com" // uncat) # Multidisciplinary Association for Psychedelic Studies
(fromDb "omegataupodcast.net" // tech) # 3/4 German; 1/4 eps are English
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol) # Maggie Killjoy -- referenced by Cory Doctorow
## Astral Codex Ten
(fromDb "sscpodcast.libsyn.com" // rat)
## Econ Talk
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
## Cory Doctorow -- both podcast & text entries
(fromDb "craphound.com" // pol)
(fromDb "congressionaldish.libsyn.com" // pol)
## 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)
(fromDb "allinchamathjason.libsyn.com" // pol)
(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
(mkPod "https://feeds.simplecast.com/54nAGcIl" // pol // daily)
# The Intercept - Intercepted; also available: <https://rss.acast.com/intercepted-with-jeremy-scahill>
(fromDb "rss.prod.firstlook.media/intercepted/podcast.rss" // pol)
(fromDb "podcast.posttv.com/itunes/post-reports.xml" // pol)
(fromDb "podcast.thelinuxexp.com" // tech)
(fromDb "politicalorphanage.libsyn.com" // pol)
(fromDb "reverseengineering.libsyn.com/rss" // tech) # UnNamed Reverse Engineering Podcast
(fromDb "rss.acast.com/deconstructed") # The Intercept - Deconstructed
## Eric Weinstein
(fromDb "rss.art19.com/the-portal" // rat)
(fromDb "darknetdiaries.com" // tech)
## Radiolab -- also available here, but ONLY OVER HTTP: <http://feeds.wnyc.org/radiolab>
(fromDb "feeds.feedburner.com/radiolab" // pol)
## Sam Harris
(fromDb "wakingup.libsyn.com" // pol)
## 99% Invisible -- also available here: <https://feeds.simplecast.com/BqbsxVfO>
(fromDb "feeds.99percentinvisible.org/99percentinvisible" // pol)
(fromDb "rss.acast.com/ft-tech-tonic" // tech)
(fromDb "rss.acast.com/intercepted-with-jeremy-scahill") # The Intercept - Intercepted
(fromDb "feeds.feedburner.com/dancarlin/history" // rat)
(fromDb "rss.art19.com/60-minutes" // pol)
(fromDb "rss.art19.com/the-portal" // rat) # Eric Weinstein
## The Verge - Decoder
(fromDb "feeds.megaphone.fm/recodedecode" // tech)
## Matrix (chat) Live
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
(fromDb "rss.art19.com/your-welcome" // pol)
(fromDb "seattlenice.buzzsprout.com" // pol)
(fromDb "sharkbytes.transistor.fm" // tech) # Wireshark Podcast o_0
(fromDb "sscpodcast.libsyn.com" // rat) # Astral Codex Ten
(fromDb "talesfromthebridge.buzzsprout.com" // tech) # Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
(fromDb "techwontsave.us" // pol) # rec by Cory Doctorow
# (fromDb "trashfuturepodcast.podbean.com" // pol) # rec by Cory Doctorow, but way rambly
(fromDb "wakingup.libsyn.com" // pol) # Sam Harris
(fromDb "werenotwrong.fireside.fm" // pol)
# (fromDb "rss.art19.com/your-welcome" // pol) # Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
# (fromDb "rss.prod.firstlook.media/deconstructed/podcast.rss" // pol) #< possible URL rot
# (fromDb "rss.prod.firstlook.media/intercepted/podcast.rss" // pol) #< possible URL rot
# (mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent) # Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
# (mkPod "https://audioboom.com/channels/5097784.rss" // tech) # Lateral with Tom Scott
# (mkPod "https://feeds.megaphone.fm/RUNMED9919162779" // pol // infrequent) # The Witch Trials of J.K. Rowling: <https://www.thefp.com/witchtrials>
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
];
texts = [
# AGGREGATORS (> 1 post/day)
(fromDb "lwn.net" // tech)
# (fromDb "lesswrong.com" // rat)
# (fromDb "econlib.org" // pol)
(fromDb "lesswrong.com" // rat)
(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)
@@ -134,14 +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 "amosbbatto.wordpress.com" // tech)
(fromDb "fasterthanli.me" // 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
@@ -150,23 +122,15 @@ let
(fromDb "vitalik.ca" // tech)
## ian (Sanctuary)
(fromDb "sagacioussuricata.com" // tech)
(fromDb "artemis.sh" // tech)
## Bunnie Juang
(fromDb "bunniestudios.com" // tech)
(fromDb "blog.danieljanus.pl" // tech)
(fromDb "ianthehenry.com" // tech)
(fromDb "bitbashing.io" // tech)
(fromDb "idiomdrottning.org" // uncat)
(mkText "http://boginjr.com/feed" // tech // infrequent)
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
(fromDb "jefftk.com" // tech)
(fromDb "pomeroyb.com" // tech)
(fromDb "harihareswara.net" // tech // pol) # rec by Cory Doctorow
(fromDb "mako.cc/copyrighteous" // tech // pol) # rec by Cory Doctorow
# (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
@@ -183,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
@@ -192,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)
@@ -211,16 +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)
(fromDb "mindingourway.com" // rat)
## 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,10 +184,8 @@ let
images = [
(fromDb "smbc-comics.com" // img // humor)
(fromDb "xkcd.com" // img // humor)
(fromDb "turnoff.us" // 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

@@ -1,143 +1,74 @@
# docs
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
{ pkgs, ... }:
{ lib, pkgs, sane-lib, ... }:
let sshOpts = rec {
fsType = "fuse.sshfs";
optionsBase = [
"x-systemd.automount"
"_netdev"
"user"
"identityfile=/home/colin/.ssh/id_ed25519"
"allow_other"
"default_permissions"
];
optionsColin = optionsBase ++ [
"transform_symlinks"
"idmap=user"
"uid=1000"
"gid=100"
];
let
fsOpts = rec {
common = [
"_netdev"
"noatime"
"user" # allow any user with access to the device to mount the fs
"x-systemd.requires=network-online.target"
"x-systemd.after=network-online.target"
"x-systemd.mount-timeout=10s" # how long to wait for mount **and** how long to wait for unmount
];
auto = [ "x-systemd.automount" ];
noauto = [ "noauto" ]; # don't mount as part of remote-fs.target
wg = [
"x-systemd.requires=wireguard-wg-home.service"
"x-systemd.after=wireguard-wg-home.service"
];
ssh = common ++ [
"identityfile=/home/colin/.ssh/id_ed25519"
"allow_other"
"default_permissions"
];
sshColin = ssh ++ [
"transform_symlinks"
"idmap=user"
"uid=1000"
"gid=100"
];
sshRoot = ssh ++ [
# we don't transform_symlinks because that breaks the validity of remote /nix stores
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
];
# in the event of hunt NFS mounts, consider:
# - <https://unix.stackexchange.com/questions/31979/stop-broken-nfs-mounts-from-locking-a-directory>
# NFS options: <https://linux.die.net/man/5/nfs>
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
# bg = retry failed mounts in the background
# retry=n = for how many minutes `mount` will retry NFS mount operation
# soft = on "major timeout", report I/O error to userspace
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
nfs = common ++ [
# "actimeo=10"
"bg"
"retrans=4"
"retry=0"
"soft"
"timeo=15"
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
];
};
remoteHome = host: {
fileSystems."/mnt/${host}-home" = {
device = "colin@${host}:/home/colin";
fsType = "fuse.sshfs";
options = fsOpts.sshColin ++ fsOpts.noauto;
noCheck = true;
};
sane.fs."/mnt/${host}-home" = sane-lib.fs.wantedDir;
};
optionsRoot = optionsBase ++ [
# we don't transform_symlinks because that breaks the validity of remote /nix stores
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
];
};
in
lib.mkMerge [
{
# 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";
{
environment.pathsToLink = [
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
# we can only link whole directories here, even though we're only interested in pkgs.openssh
"/libexec"
];
# in-memory compressed RAM
# defaults to compressing at most 50% size of RAM
# claimed compression ratio is about 2:1
# - but on moby w/ zstd default i see 4-7:1 (ratio lowers as it fills)
# note that idle overhead is about 0.05% of capacity (e.g. 2B per 4kB page)
# docs: <https://www.kernel.org/doc/Documentation/blockdev/zram.txt>
#
# to query effectiveness:
# `cat /sys/block/zram0/mm_stat`. whitespace separated fields:
# - *orig_data_size* (bytes)
# - *compr_data_size* (bytes)
# - mem_used_total (bytes)
# - mem_limit (bytes)
# - mem_used_max (bytes)
# - *same_pages* (pages which are e.g. all zeros (consumes no additional mem))
# - *pages_compacted* (pages which have been freed thanks to compression)
# - huge_pages (incompressible)
#
# see also:
# - `man zramctl`
zramSwap.enable = true;
# how much ram can be swapped into the zram device.
# this shouldn't be higher than the observed compression ratio.
# the default is 50% (why?)
# 100% should be "guaranteed" safe so long as the data is even *slightly* compressible.
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
zramSwap.memoryPercent = 100;
fileSystems."/mnt/servo-media-wan" = {
device = "colin@uninsane.org:/var/lib/uninsane/media";
inherit (sshOpts) fsType;
options = sshOpts.optionsColin;
noCheck = true;
};
fileSystems."/mnt/servo-media-lan" = {
device = "colin@servo:/var/lib/uninsane/media";
inherit (sshOpts) fsType;
options = sshOpts.optionsColin;
noCheck = true;
};
fileSystems."/mnt/servo-root-wan" = {
device = "colin@uninsane.org:/";
inherit (sshOpts) fsType;
options = sshOpts.optionsRoot;
noCheck = true;
};
fileSystems."/mnt/servo-root-lan" = {
device = "colin@servo:/";
inherit (sshOpts) fsType;
options = sshOpts.optionsRoot;
noCheck = true;
};
fileSystems."/mnt/desko-home" = {
device = "colin@desko:/home/colin";
inherit (sshOpts) fsType;
options = sshOpts.optionsColin;
noCheck = true;
};
fileSystems."/mnt/desko-root" = {
device = "colin@desko:/";
inherit (sshOpts) fsType;
options = sshOpts.optionsRoot;
noCheck = true;
};
# fileSystems."/mnt/servo-nfs" = {
# device = "servo-hn:/";
# noCheck = true;
# fsType = "nfs";
# options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
# };
fileSystems."/mnt/servo-nfs/media" = {
device = "servo-hn:/media";
noCheck = true;
fsType = "nfs";
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
};
fileSystems."/mnt/servo-nfs/playground" = {
device = "servo-hn:/playground";
noCheck = true;
fsType = "nfs";
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
};
# fileSystems."/mnt/servo-media-nfs" = {
# device = "servo-hn:/media";
# noCheck = true;
# fsType = "nfs";
# options = fsOpts.common ++ fsOpts.auto;
# };
sane.fs."/mnt/servo-media" = sane-lib.fs.wantedSymlinkTo "/mnt/servo-nfs/media";
environment.pathsToLink = [
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
# we can only link whole directories here, even though we're only interested in pkgs.openssh
"/libexec"
];
environment.systemPackages = [
pkgs.sshfs-fuse
];
}
(remoteHome "desko")
(remoteHome "lappy")
(remoteHome "moby")
]
environment.systemPackages = [
pkgs.sshfs-fuse
];
}

40
hosts/common/hardware.nix Normal file
View File

@@ -0,0 +1,40 @@
{ lib, pkgs, ... }:
{
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
# useful emergency utils
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfstune
'';
boot.kernelParams = [ "boot.shell_on_fail" ];
# other kernelParams:
# "boot.trace"
# "systemd.log_level=debug"
# "systemd.log_target=console"
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
boot.initrd.preFailCommands = "allowShell=1";
# default: 4 (warn). 7 is debug
boot.consoleLogLevel = 7;
boot.loader.grub.enable = lib.mkDefault false;
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
# non-free firmware
hardware.enableRedistributableFirmware = true;
services.fwupd.enable = true;
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
powerManagement.powertop.enable = false;
# services.snapper.configs = {
# root = {
# subvolume = "/";
# extraConfig = {
# ALLOW_USERS = "colin";
# };
# };
# };
# services.snapper.snapshotInterval = "daily";
}

View File

@@ -1,73 +0,0 @@
{ lib, pkgs, ... }:
{
imports = [
./x86_64.nix
];
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
# useful emergency utils
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.btrfs-progs}/bin/btrfstune
copy_bin_and_libs ${pkgs.util-linux}/bin/{cfdisk,lsblk,lscpu}
copy_bin_and_libs ${pkgs.gptfdisk}/bin/{cgdisk,gdisk}
copy_bin_and_libs ${pkgs.smartmontools}/bin/smartctl
copy_bin_and_libs ${pkgs.nvme-cli}/bin/nvme
copy_bin_and_libs ${pkgs.e2fsprogs}/bin/resize2fs
'';
boot.kernelParams = [
"boot.shell_on_fail"
#v experimental full pre-emption for hopefully better call/audio latency on moby.
# also toggleable at runtime via /sys/kernel/debug/sched/preempt
# defaults to preempt=voluntary
# "preempt=full"
];
# other kernelParams:
# "boot.trace"
# "systemd.log_level=debug"
# "systemd.log_target=console"
# hack in the `boot.shell_on_fail` arg since that doesn't always seem to work.
boot.initrd.preFailCommands = "allowShell=1";
# default: 4 (warn). 7 is debug
boot.consoleLogLevel = 7;
boot.loader.grub.enable = lib.mkDefault false;
boot.loader.generic-extlinux-compatible.enable = lib.mkDefault true;
# non-free firmware
hardware.enableRedistributableFirmware = true;
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
powerManagement.powertop.enable = false;
# linux CPU governor: <https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt>
# - options:
# - "powersave" => force CPU to always run at lowest supported frequency
# - "performance" => force CPU to always run at highest frequency
# - "ondemand" => adjust frequency based on load
# - "conservative" (ondemand but slower to adjust)
# - "schedutil"
# - "userspace"
# - not all options are available for all platforms
# - intel (intel_pstate) appears to manage scaling w/o intervention/control from the OS.
# - AMD (acpi-cpufreq) appears to manage scaling via the OS *or* HW. but the ondemand defaults never put it to max hardware frequency.
# - qualcomm (cpufreq-dt) appears to manage scaling *only* via the OS. ondemand governor exercises the full range.
# - query details with `sudo cpupower frequency-info`
powerManagement.cpuFreqGovernor = "ondemand";
services.logind.extraConfig = ''
# dont shutdown when power button is short-pressed
HandlePowerKey=ignore
'';
# services.snapper.configs = {
# root = {
# subvolume = "/";
# extraConfig = {
# ALLOW_USERS = "colin";
# };
# };
# };
# services.snapper.snapshotInterval = "daily";
}

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 = [
./keyring
./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,203 @@
# 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;
# 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=";
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-52lYqMjrS3GVTaybDrH1p6VF90YVkifguCGxobI/fNQ=";
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
}
);
}

18
hosts/common/home/git.nix Normal file
View File

@@ -0,0 +1,18 @@
{ lib, pkgs, sane-lib, ... }:
let
mkCfg = lib.generators.toINI { };
in
{
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
});
}

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

@@ -0,0 +1,11 @@
{ config, sane-lib, ... }:
{
sane.user.persist.private = [ ".local/share/keyrings" ];
sane.user.fs."private/.local/share/keyrings/default" = {
generated.script.script = builtins.readFile ../../../scripts/init-keyring;
# TODO: is this `wantedBy` needed? can we inherit it?
wantedBy = [ config.sane.fs."/home/colin/private".unit ];
};
}

View File

@@ -1,17 +0,0 @@
{ config, pkgs, sane-lib, ... }:
let
init-keyring = pkgs.static-nix-shell.mkBash {
pname = "init-keyring";
src = ./.;
};
in
{
sane.user.persist.byStore.private = [ ".local/share/keyrings" ];
sane.user.fs."private/.local/share/keyrings/default" = {
generated.command = [ "${init-keyring}/bin/init-keyring" ];
wantedBy = [ config.sane.fs."/home/colin/private".unit ];
wantedBeforeBy = [ ]; # don't created this as part of `multi-user.target`
};
}

View File

@@ -0,0 +1,68 @@
{ pkgs, sane-lib, ... }:
{
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 ${pkgs.kitty-themes}/themes/PaperColor_dark.conf
'';
# THEME CHOICES:
# docs: https://github.com/kovidgoyal/kitty-themes
# theme = "1984 Light"; # dislike: awful, harsh blues/teals
# theme = "Adventure Time"; # dislike: harsh (dark)
# theme = "Atom One Light"; # GOOD: light theme. all color combos readable. not a huge fan of the blue.
# theme = "Belafonte Day"; # dislike: too low contrast for text colors
# theme = "Belafonte Night"; # better: dark theme that's easy on the eyes. all combos readable. low contrast.
# theme = "Catppuccin"; # dislike: a bit pale/low-contrast (dark)
# theme = "Desert"; # mediocre: colors are harsh
# theme = "Earthsong"; # BEST: dark theme. readable, good contrast. unique, but decent colors.
# theme = "Espresso Libre"; # better: dark theme. readable, but meh colors
# theme = "Forest Night"; # decent: very pastel. it's workable, but unconventional and muted/flat.
# theme = "Gruvbox Material Light Hard"; # mediocre light theme.
# theme = "kanagawabones"; # better: dark theme. colors are too background-y
# theme = "Kaolin Dark"; # dislike: too dark
# theme = "Kaolin Breeze"; # mediocre: not-too-harsh light theme, but some parts are poor contrast
# theme = "Later This Evening"; # mediocre: not-too-harsh dark theme, but cursor is poor contrast
# theme = "Material"; # decent: light theme, few colors.
# theme = "Mayukai"; # decent: not-too-harsh dark theme. the teal is a bit straining
# theme = "Nord"; # mediocre: pale background, low contrast
# theme = "One Half Light"; # better: not-too-harsh light theme. contrast could be better
# theme = "PaperColor Dark"; # BEST: dark theme, very readable still the colors are background-y
# theme = "Parasio Dark"; # dislike: too low contrast
# theme = "Pencil Light"; # better: not-too-harsh light theme. decent contrast.
# theme = "Pnevma"; # dislike: too low contrast
# theme = "Piatto Light"; # better: readable light theme. pleasing colors. powerline prompt is hard to read.
# theme = "Rosé Pine Dawn"; # GOOD: light theme. all color combinations are readable. it is very mild -- may need to manually tweak contrast. tasteful colors
# theme = "Rosé Pine Moon"; # GOOD: dark theme. tasteful colors. but background is a bit intense
# theme = "Sea Shells"; # mediocre. not all color combos are readable
# theme = "Solarized Light"; # mediocre: not-too-harsh light theme; GREAT background; but some colors are low contrast
# theme = "Solarized Dark Higher Contrast"; # better: dark theme, decent colors
# theme = "Sourcerer"; # mediocre: ugly colors
# theme = "Space Gray"; # mediocre: too muted
# theme = "Space Gray Eighties"; # better: all readable, decent colors
# theme = "Spacemacs"; # mediocre: too muted
# theme = "Spring"; # mediocre: readable light theme, but the teal is ugly.
# theme = "Srcery"; # better: highly readable. colors are ehhh
# theme = "Substrata"; # decent: nice colors, but a bit flat.
# theme = "Sundried"; # mediocre: the solar text makes me squint
# theme = "Symfonic"; # mediocre: the dark purple has low contrast to the black bg.
# theme = "Tango Light"; # dislike: teal is too grating
# theme = "Tokyo Night Day"; # medicore: too muted
# theme = "Tokyo Night"; # better: tasteful. a bit flat
# theme = "Tomorrow"; # GOOD: all color combinations are readable. contrast is slightly better than Rose. on the blander side
# theme = "Treehouse"; # dislike: the orange is harsh on my eyes.
# theme = "Urple"; # dislike: weird palette
# theme = "Warm Neon"; # decent: not-too-harsh dark theme. the green is a bit unattractive
# theme = "Wild Cherry"; # GOOD: dark theme: nice colors. a bit flat
# theme = "Xcodedark"; # dislike: bad palette
# theme = "citylights"; # decent: dark theme. some parts have just a bit low contrast
# theme = "neobones_light"; # better light theme. the background is maybe too muted
# theme = "vimbones";
# theme = "zenbones_dark"; # mediocre: readable, but meh colors
# theme = "zenbones_light"; # decent: light theme. all colors are readable. contrast is passable but not excellent. highlight color is BAD
# theme = "zenwritten_dark"; # mediocre: looks same as zenbones_dark
}

View File

@@ -0,0 +1,14 @@
{ sane-lib, ... }:
{
# libreoffice: disable first-run stuff
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>
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="ShowTipOfTheDay" oor:op="fuse"><value>false</value></prop></item>
</oor:items>
'';
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeDonateShown" oor:op="fuse"><value>1667693880</value></prop></item>
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeGetInvolvedShown" oor:op="fuse"><value>1667693880</value></prop></item>
}

View File

@@ -1,28 +1,42 @@
{ config, lib, ...}:
{ config, sane-lib, ...}:
let
# ProgramConfig -> { "<mime-type>" = { priority, desktop }; }
weightedMimes = prog: builtins.mapAttrs (_key: desktop: { priority = prog.mime.priority; desktop = desktop; }) prog.mime.associations;
# [ { "<mime-type>" = { priority, desktop } ]; } ] -> { "<mime-type>" = [ { priority, desktop } ... ]; }
mergeMimes = mimes: lib.foldAttrs (item: acc: [item] ++ acc) [] mimes;
# [ { priority, desktop } ... ] -> Self
sortOneMimeType = associations: builtins.sort (l: r: assert l.priority != r.priority; l.priority < r.priority) associations;
sortMimes = mimes: builtins.mapAttrs (_k: sortOneMimeType) mimes;
removePriorities = mimes: builtins.mapAttrs (_k: associations: builtins.map (a: a.desktop) associations) mimes;
# [ ProgramConfig ]
enabledPrograms = builtins.filter (p: p.enabled) (builtins.attrValues config.sane.programs);
# [ { "<mime-type>" = { prority, desktop } ]
enabledWeightedMimes = builtins.map weightedMimes enabledPrograms;
www = config.sane.web-browser.browser.desktop;
pdf = "org.gnome.Evince.desktop";
md = "obsidian.desktop";
thumb = "org.gnome.gThumb.desktop";
video = "vlc.desktop";
# audio = "mpv.desktop";
audio = "vlc.desktop";
in
{
# the xdg mime type for a file can be found with:
# - `xdg-mime query filetype path/to/thing.ext`
# the default handler for a mime type can be found with:
# - `xdg-mime query default <mimetype>` (e.g. x-scheme-handler/http)
#
# we can have single associations or a list of associations.
# there's also options to *remove* [non-default] associations from specific apps
xdg.mime.enable = true;
xdg.mime.defaultApplications = removePriorities (sortMimes (mergeMimes enabledWeightedMimes));
xdg.mime.defaultApplications = {
# AUDIO
"audio/flac" = audio;
"audio/mpeg" = audio;
"audio/x-vorbis+ogg" = audio;
# IMAGES
"image/heif" = thumb; # apple codec
"image/png" = thumb;
"image/jpeg" = thumb;
# VIDEO
"video/mp4" = video;
"video/quicktime" = video;
"video/x-matroska" = video;
# HTML
"text/html" = www;
"x-scheme-handler/http" = www;
"x-scheme-handler/https" = www;
"x-scheme-handler/about" = www;
"x-scheme-handler/unknown" = www;
# RICH-TEXT DOCUMENTS
"application/pdf" = pdf;
"text/markdown" = md;
};
}

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,25 +1,39 @@
{ 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: fzf-vim (fuzzy finder): https://github.com/junegunn/fzf.vim
plugin = fzf-vim;
}
{
# docs: surround-nvim: https://github.com/ur4ltz/surround.nvim/
# docs: vim-surround: https://github.com/tpope/vim-surround
{ plugin = vim-surround; }
# docs: fzf-vim (fuzzy finder): https://github.com/junegunn/fzf.vim
{ plugin = fzf-vim; }
({
# docs: tex-conceal-vim: https://github.com/KeitaNakamura/tex-conceal.vim/
plugin = tex-conceal-vim;
type = "viml";
config = ''
" present prettier fractions
let g:tex_conceal_frac=1
'';
})
({
plugin = vim-SyntaxRange;
type = "viml";
config = ''
" enable markdown-style codeblock highlighting for tex code
autocmd BufEnter * call SyntaxRange#Include('```tex', '```', 'tex', 'NonText')
" autocmd Syntax tex set conceallevel=2
'';
})
({
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
# docs: https://github.com/nvim-treesitter/nvim-treesitter
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
# this is required for tree-sitter to even highlight
plugin = nvim-treesitter.withPlugins (_: nvim-treesitter.allGrammars ++ [
# XXX: this is apparently not enough to enable syntax highlighting!
# nvim-treesitter ships its own queries which may be distinct from e.g. helix.
# the queries aren't included when i ship the grammar in this manner
pkgs.tree-sitter-nix-shell
]);
plugin = nvim-treesitter.withAllGrammars;
type = "lua";
config = ''
require'nvim-treesitter.configs'.setup {
@@ -50,60 +64,23 @@ let
vim.o.foldmethod = 'expr'
vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
'';
}
{
# docs: tex-conceal-vim: https://github.com/KeitaNakamura/tex-conceal.vim/
plugin = tex-conceal-vim;
type = "viml";
config = ''
" present prettier fractions
let g:tex_conceal_frac=1
'';
}
{
# source: <https://github.com/LnL7/vim-nix>
# fixes auto-indent (incl tab size) when editing .nix files
plugin = vim-nix;
}
{
# docs: surround-nvim: https://github.com/ur4ltz/surround.nvim/
# docs: vim-surround: https://github.com/tpope/vim-surround
plugin = vim-surround;
}
{
plugin = vim-SyntaxRange;
type = "viml";
config = ''
" enable markdown-style codeblock highlighting for tex code
autocmd BufEnter * call SyntaxRange#Include('```tex', '```', 'tex', 'NonText')
" autocmd Syntax tex set conceallevel=2
'';
}
})
];
plugin-packages = map (p: p.plugin) plugins;
plugin-config-viml = concatMapStrings (p: optionalString (p.type or "" == "viml") p.config) plugins;
plugin-config-tex = concatMapStrings (p: optionalString (p.type or "" == "viml") p.config) plugins;
plugin-config-lua = concatMapStrings (p: optionalString (p.type or "" == "lua") p.config) plugins;
in
{
# private because there could be sensitive things in the swap
sane.programs.neovim = {
persist.byStore.private = [ ".cache/vim-swap" ];
env.EDITOR = "vim";
# git claims it should use EDITOR, but it doesn't!
env.GIT_EDITOR = "vim";
mime.priority = 200; # default=100 => yield to other, more specialized applications
mime.associations."application/schema+json" = "nvim.desktop";
mime.associations."plain/text" = "nvim.desktop";
mime.associations."text/markdown" = "nvim.desktop";
};
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;
vimAlias = true;
configure = {
packages.plugins = {
packages.myVimPackage = {
start = plugin-packages;
};
customRC = ''
@@ -139,8 +116,8 @@ in
set list
set listchars=tab:\·,trail:·,extends:,precedes:,nbsp:
""""" PLUGIN CONFIG (vim)
${plugin-config-viml}
""""" PLUGIN CONFIG (tex)
${plugin-config-tex}
""""" PLUGIN CONFIG (lua)
lua <<EOF

View File

@@ -0,0 +1,12 @@
# news-flash RSS viewer
{ config, 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/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

@@ -1,29 +1,24 @@
# TODO: this should be moved to users/colin.nix
{ config, lib, ... }:
{ config, lib, sane-lib, ... }:
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;
host-keys = lib.filter (k: k.user == "root") (lib.attrValues config.sane.ssh.pubkeys);
known-hosts-text = lib.concatStringsSep
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"
(builtins.map (k: k.asHostKey) host-keys)
(map (k: k.asHostKey) host-keys)
;
in
{
# ssh key is stored in private storage
sane.user.persist.byStore.private = [
{ type = "file"; path = ".ssh/id_ed25519"; }
];
sane.user.fs.".ssh/id_ed25519.pub" = lib.mkIf (user-pubkey != null) {
symlink.text = user-pubkey;
};
sane.user.fs.".ssh/known_hosts".symlink.text = known-hosts-text;
sane.user.persist.private = [ ".ssh/id_ed25519" ];
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 =
let
user-keys = lib.filter (k: k.user == "colin") (lib.attrValues config.sane.ssh.pubkeys);
user-keys = filter (k: k.user == "colin") (attrValues config.sane.ssh.pubkeys);
in
builtins.map (k: k.asUserKey) user-keys;
map (k: k.asUserKey) user-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

@@ -1,9 +1,9 @@
{ ... }:
{ lib, sane-lib, ...}:
{
# XDG defines things like ~/Desktop, ~/Downloads, etc.
# these clutter the home, so i mostly don't use them.
sane.user.fs.".config/user-dirs.dirs".symlink.text = ''
sane.user.fs.".config/user-dirs.dirs" = sane-lib.fs.wantedText ''
XDG_DESKTOP_DIR="$HOME/.xdg/Desktop"
XDG_DOCUMENTS_DIR="$HOME/dev"
XDG_DOWNLOAD_DIR="$HOME/tmp"
@@ -16,5 +16,5 @@
# prevent `xdg-user-dirs-update` from overriding/updating our config
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
sane.user.fs.".config/user-dirs.conf".symlink.text = "enabled=False";
sane.user.fs.".config/user-dirs.conf" = sane-lib.fs.wantedText "enabled=False";
}

View File

@@ -0,0 +1,148 @@
{ 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";
}
expiration=$(date -d "6 Mar" +%s)
today=$(date +%s)
days_until=$(( ($expiration - $today) / (24*60*60) ))
echo "You have $days_until days to renew your driver's license"
# 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'
'';
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
{ lib, ... }:
{
# TODO: this should be populated per-host
sane.hosts.by-name."desko" = {
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
wg-home.ip = "10.0.10.22";
lan-ip = "10.78.79.52";
};
sane.hosts.by-name."lappy" = {
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
wg-home.ip = "10.0.10.20";
lan-ip = "10.78.79.53";
};
sane.hosts.by-name."moby" = {
ssh.authorized = lib.mkDefault false; # moby's too easy to hijack: don't let it ssh places
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
wg-home.pubkey = "I7XIR1hm8bIzAtcAvbhWOwIAabGkuEvbWH/3kyIB1yA=";
wg-home.ip = "10.0.10.48";
lan-ip = "10.78.79.54";
};
sane.hosts.by-name."servo" = {
ssh.authorized = lib.mkDefault false; # servo presents too many services to the internet: easy atack vector
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
wg-home.ip = "10.0.10.5";
wg-home.endpoint = "uninsane.org:51820";
lan-ip = "10.78.79.51";
};
}

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