Compare commits

..

1 Commits

Author SHA1 Message Date
42cb2c7239 import and update Tokodon package from <https://github.com/NixOS/nixpkgs/pull/170466>
this hangs and then segfaults at start. i suppose i should `gdb` it.
2022-10-30 18:23:18 -07:00
246 changed files with 2793 additions and 9814 deletions

151
flake.lock generated
View File

@@ -22,11 +22,11 @@
]
},
"locked": {
"lastModified": 1667907331,
"narHash": "sha256-bHkAwkYlBjkupPUFcQjimNS8gxWSWjOTevEuwdnp5m0=",
"lastModified": 1665996265,
"narHash": "sha256-/k9og6LDBQwT+f/tJ5ClcWiUl8kCX5m6ognhsAxOiCY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "6639e3a837fc5deb6f99554072789724997bc8e5",
"rev": "b81e128fc053ab3159d7b464d9b7dedc9d6a6891",
"type": "github"
},
"original": {
@@ -36,14 +36,29 @@
"type": "github"
}
},
"impermanence": {
"locked": {
"lastModified": 1661933071,
"narHash": "sha256-RFgfzldpbCvS+H2qwH+EvNejvqs+NhPVD5j1I7HQQPY=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "def994adbdfc28974e87b0e4c949e776207d5557",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "impermanence",
"type": "github"
}
},
"mobile-nixos": {
"flake": false,
"locked": {
"lastModified": 1670131242,
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
"lastModified": 1666573922,
"narHash": "sha256-CqB8Y5HajptSFE8Em990dcYZIHJWBiO9zd1us4Mzx8M=",
"owner": "nixos",
"repo": "mobile-nixos",
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
"rev": "1351091d2537040454fa232d8b94e745ab0eb5a3",
"type": "github"
},
"original": {
@@ -53,60 +68,12 @@
}
},
"nixpkgs": {
"inputs": {
"nixpkgs": [
"nixpkgs-unpatched"
]
},
"locked": {
"lastModified": 1,
"narHash": "sha256-FaeqnQPm3MIXkQsNsGTB4A9lN82YKpLkmVHgdJyJNGg=",
"path": "nixpatches",
"type": "path"
},
"original": {
"path": "nixpatches",
"type": "path"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1673527292,
"narHash": "sha256-903EpRSDCfUvic7Hsiqwy+h7zlMTLAUbCXkEGGriCfM=",
"lastModified": 1666447894,
"narHash": "sha256-i9WHX4w/et4qPMzEXd9POmnO0/bthjr7R4cblKNHGms=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6a3f9996408c970b99b8b992b11bb249d1455b62",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-22.11",
"type": "indirect"
}
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1673100377,
"narHash": "sha256-mT76pTd0YFxT6CwtPhDgHJhuIgLY+ZLSMiQpBufwMG4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9f11a2df77cb945c115ae2a65f53f38121597d73",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unpatched": {
"locked": {
"lastModified": 1673450908,
"narHash": "sha256-b8em+kwrNtnB7gR8SyVf6WuTyQ+6tHS6dzt9D9wgKF0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6c8644fc37b6e141cbfa6c7dc8d98846c4ff0c2e",
"rev": "95aeaf83c247b8f5aa561684317ecd860476fcd6",
"type": "github"
},
"original": {
@@ -115,15 +82,63 @@
"type": "indirect"
}
},
"nixpkgs-22_05": {
"locked": {
"lastModified": 1666488099,
"narHash": "sha256-DANs2epN5QgvxWzH7xF3dzb4WE0lEuMLrMEu/vPmQxw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f9115594149ebcb409a42e303bec4956814a8419",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1666401273,
"narHash": "sha256-AG3MoIjcWwz1SPjJ2nymWu4NmeVj9P40OpB1lsmxFtg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3933d8bb9120573c0d8d49dc5e890cb211681490",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-22.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"home-manager": "home-manager",
"impermanence": "impermanence",
"mobile-nixos": "mobile-nixos",
"nixpkgs": "nixpkgs",
"nixpkgs-stable": "nixpkgs-stable",
"nixpkgs-unpatched": "nixpkgs-unpatched",
"rycee": "rycee",
"sops-nix": "sops-nix",
"uninsane-dot-org": "uninsane-dot-org"
"uninsane": "uninsane"
}
},
"rycee": {
"flake": false,
"locked": {
"lastModified": 1666843362,
"narHash": "sha256-xn2bW9/MT0u8Ptlk+f323p46Q/ktZkzMp7oj5SlYDxU=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "43d3a363c126968db46585b88b8eb97dd32634ad",
"type": "gitlab"
},
"original": {
"owner": "rycee",
"repo": "nur-expressions",
"type": "gitlab"
}
},
"sops-nix": {
@@ -131,14 +146,14 @@
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable_2"
"nixpkgs-22_05": "nixpkgs-22_05"
},
"locked": {
"lastModified": 1673481602,
"narHash": "sha256-P80X38fOM2MtoYdgkyuksGOQPDhIhNJW2W2jMeMIZzE=",
"lastModified": 1666499473,
"narHash": "sha256-q1eFnBFL0kHgcnUPeKagw3BfbE/5sMJNGL2E2AR+a2M=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "32187b33ac6ec9b628dcd08dd941a715e6241dda",
"rev": "1b5f9512a265f0c9687dbff47893180f777f4809",
"type": "github"
},
"original": {
@@ -147,7 +162,7 @@
"type": "github"
}
},
"uninsane-dot-org": {
"uninsane": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
@@ -155,11 +170,11 @@
]
},
"locked": {
"lastModified": 1666870107,
"narHash": "sha256-b9eXZxSwhzdJI5uQgfrMhu4SY2POrPkinUg7F5gQVYo=",
"lastModified": 1665758541,
"narHash": "sha256-ibR8bPwHlDjavri5cNVnoo5FmFk1IfNMmQXxat5biqs=",
"ref": "refs/heads/master",
"rev": "80c6ec95bd430e29d231cf745f19279bb76fb382",
"revCount": 164,
"rev": "4ad1801f6cecd678bbeae5dfe5933448dd7b3360",
"revCount": 163,
"type": "git",
"url": "https://git.uninsane.org/colin/uninsane"
},

203
flake.nix
View File

@@ -4,12 +4,8 @@
{
inputs = {
nixpkgs-stable.url = "nixpkgs/nixos-22.11";
nixpkgs-unpatched.url = "nixpkgs/nixos-unstable";
nixpkgs = {
url = "path:nixpatches";
inputs.nixpkgs.follows = "nixpkgs-unpatched";
};
nixpkgs-stable.url = "nixpkgs/nixos-22.05";
nixpkgs.url = "nixpkgs/nixos-unstable";
mobile-nixos = {
url = "github:nixos/mobile-nixos";
flake = false;
@@ -18,11 +14,16 @@
url = "github:nix-community/home-manager/release-22.05";
inputs.nixpkgs.follows = "nixpkgs";
};
rycee = {
url = "gitlab:rycee/nur-expressions";
flake = false;
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
uninsane-dot-org = {
impermanence.url = "github:nix-community/impermanence";
uninsane = {
url = "git+https://git.uninsane.org/colin/uninsane";
inputs.nixpkgs.follows = "nixpkgs";
};
@@ -32,55 +33,59 @@
self,
nixpkgs,
nixpkgs-stable,
nixpkgs-unpatched,
mobile-nixos,
home-manager,
rycee,
sops-nix,
uninsane-dot-org
}:
impermanence,
uninsane
}: let
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
name = "nixpkgs-patched-uninsane";
src = nixpkgs;
patches = import ./nixpatches/list.nix nixpkgs.legacyPackages.${system}.fetchpatch;
};
# return something which behaves like `pkgs`, for the provided system
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
nixpkgsFor = local: target: import (patchedPkgs target) { crossSystem = target; localSystem = local; };
# evaluate ONLY our overlay, for the provided system
customPackagesFor = local: target: import ./pkgs/overlay.nix (nixpkgsFor local target) (nixpkgsFor local target);
decl-machine = { name, local, target }:
let
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
in (nixosSystem {
# by default the local system is the same as the target, employing emulation when they differ
system = target;
specialArgs = { inherit mobile-nixos home-manager impermanence; };
modules = [
./modules
(import ./machines/instantiate.nix name)
home-manager.nixosModule
impermanence.nixosModule
sops-nix.nixosModules.sops
{
nixpkgs.overlays = [
(import "${mobile-nixos}/overlay/overlay.nix")
(import "${rycee}/overlay.nix")
uninsane.overlay
(import ./pkgs/overlay.nix)
(next: prev: rec {
# 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.
cross = (nixpkgsFor local target) // (customPackagesFor local target);
stable = import nixpkgs-stable { system = target; };
# pinned packages:
electrum = stable.electrum; # 2022-10-10: build break
sequoia = stable.sequoia; # 2022-10-13: build break
})
];
}
];
});
evalHost = { name, local, target }:
let
# 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
(nixosSystem {
# we use pkgs built for and *by* the target, i.e. emulation, by default.
# cross compilation only happens on explicit access to `pkgs.cross`
system = target;
modules = [
(import ./hosts/instantiate.nix { localSystem = local; hostName = name; })
self.nixosModules.default
self.nixosModules.passthru
{
nixpkgs.overlays = [
self.overlays.default
self.overlays.passthru
];
}
];
});
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
decl-bootable-machine = { name, local, target }: rec {
nixosConfiguration = decl-machine { inherit name local target; };
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
# after building this:
# - flash it to a bootable medium (SD card, flash drive, HDD)
@@ -93,76 +98,32 @@
# - boot
# - 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 = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.img) self.nixosConfigurations;
overlays = rec {
default = pkgs;
pkgs = import ./overlays/pkgs.nix;
passthru =
let
stable = next: prev: {
stable = nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform}";
};
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
uninsane = uninsane-dot-org.overlay;
in
next: prev:
(stable next prev) // (mobile next prev) // (uninsane next prev);
};
nixosModules = rec {
default = sane;
sane = import ./modules;
passthru = { ... }: {
imports = [
home-manager.nixosModule
sops-nix.nixosModules.sops
];
};
};
# this includes both our native packages and all the nixpkgs packages.
legacyPackages =
let
allPkgsFor = sys: (nixpkgsCompiledBy sys).appendOverlays [
self.overlays.passthru self.overlays.pkgs
];
in {
x86_64-linux = allPkgsFor "x86_64-linux";
aarch64-linux = allPkgsFor "aarch64-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";
in {
update-feeds = {
type = "app";
program = "${pkgs.feeds.passthru.updateScript}";
};
init-feed = {
type = "app";
program = "${pkgs.feeds.passthru.initFeedScript}";
};
};
templates = {
python-data = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
# then enter with:
# - `nix develop`
path = ./templates/python-data;
description = "python environment for data processing";
};
};
# - `nixos-rebuild --flake './#<machine>' switch`
img = nixosConfiguration.config.system.build.img;
};
machines.servo = decl-bootable-machine { name = "servo"; local = "aarch64-linux"; target = "aarch64-linux"; };
machines.desko = decl-bootable-machine { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
machines.lappy = decl-bootable-machine { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
machines.moby = decl-bootable-machine { 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 moby-cross and then moby incurs some rebuilding.
machines.moby-cross = decl-bootable-machine { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
machines.rescue = decl-bootable-machine { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
in {
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) machines;
imgs = builtins.mapAttrs (name: value: value.img) machines;
packages = let
allPkgsFor = sys: (customPackagesFor sys sys) // {
nixpkgs = nixpkgsFor sys sys;
uninsane = uninsane.packages."${sys}";
rycee = (import "${rycee}/default.nix" { pkgs = nixpkgsFor sys sys; });
};
in {
x86_64-linux = allPkgsFor "x86_64-linux";
aarch64-linux = allPkgsFor "aarch64-linux";
};
};
}

View File

@@ -1,16 +0,0 @@
{ lib, pkgs, ... }:
{
# persist external pairings by default
sane.persist.sys.plaintext = [ "/var/lib/bluetooth" ];
sane.fs."/var/lib/bluetooth".generated.acl.mode = "0700";
sane.fs."/var/lib/bluetooth/.secrets.stamp" = {
wantedBeforeBy = [ "bluetooth.service" ];
# XXX: install-bluetooth uses sed, but that's part of the default systemd unit path, it seems
generated.script.script = builtins.readFile ../../scripts/install-bluetooth + ''
touch "/var/lib/bluetooth/.secrets.stamp"
'';
generated.script.scriptArgs = [ "/run/secrets/bt" ];
};
}

View File

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

View File

@@ -1,82 +0,0 @@
{ pkgs, ... }:
{
imports = [
./bluetooth.nix
./cross.nix
./feeds.nix
./fs.nix
./hardware
./i2p.nix
./ids.nix
./machine-id.nix
./net.nix
./secrets.nix
./ssh.nix
./users.nix
./vpn.nix
];
sane.home-manager.enable = true;
sane.nixcache.enable-trusted-keys = true;
sane.packages.enableConsolePkgs = true;
sane.packages.enableSystemPkgs = true;
sane.persist.sys.plaintext = [
"/var/log"
"/var/backup" # for e.g. postgres dumps
# TODO: move elsewhere
"/var/lib/alsa" # preserve output levels, default devices
"/var/lib/colord" # preserve color calibrations (?)
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
];
nixpkgs.config.allowUnfree = true;
# time.timeZone = "America/Los_Angeles";
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
# allow `nix flake ...` command
nix.extraOptions = ''
experimental-features = nix-command flakes
'';
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
nix.nixPath = [
"nixpkgs=${pkgs.path}"
"nixpkgs-overlays=${../..}/overlays"
];
# TODO: move this into home-manager?
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";
};
# enable zsh completions
environment.pathsToLink = [ "/share/zsh" ];
# link debug symbols into /run/current-system/sw/lib/debug
# hopefully picked up by gdb automatically?
environment.enableDebugInfo = true;
}

View File

@@ -1,186 +0,0 @@
{ lib, sane-data, ... }:
let
hourly = { freq = "hourly"; };
daily = { freq = "daily"; };
weekly = { freq = "weekly"; };
infrequent = { freq = "infrequent"; };
art = { cat = "art"; };
humor = { cat = "humor"; };
pol = { cat = "pol"; }; # or maybe just "social"
rat = { cat = "rat"; };
tech = { cat = "tech"; };
uncat = { cat = "uncat"; };
text = { format = "text"; };
mkRss = format: url: { inherit url format; } // uncat // infrequent;
# format-specific helpers
mkText = mkRss "text";
mkImg = mkRss "image";
mkPod = mkRss "podcast";
# host-specific helpers
mkSubstack = subdomain: { substack = subdomain; };
fromDb = name:
let
raw = sane-data.feeds."${name}";
in {
url = raw.url;
# not sure the exact mapping with velocity here: entries per day?
freq = lib.mkDefault (
if raw.velocity or 0 > 2 then
"hourly"
else if raw.velocity or 0 > 0.5 then
"daily"
else if raw.velocity or 0 > 0.1 then
"weekly"
else
"infrequent"
);
} // lib.optionalAttrs (raw.is_podcast or false) {
format = "podcast";
} // lib.optionalAttrs (raw.title or "" != "") {
title = lib.mkDefault raw.title;
};
podcasts = [
(fromDb "lexfridman.com/podcast" // rat)
# (mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
## 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)
(mkPod "https://congressionaldish.libsyn.com/rss" // pol // infrequent)
## Civboot -- https://anchor.fm/civboot
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
(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)
## 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 "feeds.feedburner.com/dancarlin/history" // rat)
(fromDb "rss.art19.com/60-minutes" // pol)
## 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)
];
texts = [
# AGGREGATORS (> 1 post/day)
(fromDb "lesswrong.com" // rat)
(fromDb "econlib.org" // pol)
# AGGREGATORS (< 1 post/day)
(mkText "https://palladiummag.com/feed" // uncat // weekly)
(mkText "https://profectusmag.com/feed" // uncat // weekly)
(mkText "https://semiaccurate.com/feed" // tech // weekly)
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
(mkText "https://spectrum.ieee.org/rss" // tech // weekly)
## No Moods, Ads or Cutesy Fucking Icons
(mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly)
# DEVELOPERS
(fromDb "uninsane.org" // tech)
(fromDb "mg.lol" // tech)
## Ken Shirriff
(fromDb "righto.com" // tech)
## Vitalik Buterin
(mkText "https://vitalik.ca/feed.xml" // tech // infrequent)
## ian (Sanctuary)
(mkText "https://sagacioussuricata.com/feed.xml" // tech // infrequent)
## Bunnie Juang
(mkText "https://www.bunniestudios.com/blog/?feed=rss2" // tech // infrequent)
(mkText "https://blog.danieljanus.pl/atom.xml" // tech // infrequent)
(mkText "https://ianthehenry.com/feed.xml" // tech // infrequent)
(mkText "https://bitbashing.io/feed.xml" // tech // infrequent)
(mkText "https://idiomdrottning.org/feed.xml" // uncat // daily)
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
(mkText "https://www.jefftk.com/news.rss" // tech // daily)
(mkText "https://pomeroyb.com/feed.xml" // tech // infrequent)
# (TECH; POL) COMMENTATORS
(fromDb "edwardsnowden.substack.com" // pol // text)
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
## Ben Thompson
(mkText "https://www.stratechery.com/rss" // pol // weekly)
## Balaji
(mkText "https://balajis.com/rss" // pol // weekly)
(mkText "https://www.ben-evans.com/benedictevans/rss.xml" // pol // weekly)
(mkText "https://www.lynalden.com/feed" // pol // infrequent)
(mkText "https://austinvernon.site/rss.xml" // tech // infrequent)
(mkSubstack "oversharing" // pol // daily)
(mkSubstack "doomberg" // tech // weekly)
## David Rosenthal
(mkText "https://blog.dshr.org/rss.xml" // pol // weekly)
## Matt Levine
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
(mkText "https://stpeter.im/atom.xml" // pol // weekly)
# RATIONALITY/PHILOSOPHY/ETC
(mkSubstack "samkriss" // humor // infrequent)
(mkText "https://unintendedconsequenc.es/feed" // rat // infrequent)
(mkText "https://applieddivinitystudies.com/atom.xml" // rat // weekly)
(mkText "https://slimemoldtimemold.com/feed.xml" // rat // weekly)
(mkText "https://www.richardcarrier.info/feed" // rat // weekly)
(mkText "https://www.gwern.net/feed.xml" // uncat // infrequent)
## Jason Crawford
(mkText "https://rootsofprogress.org/feed.xml" // rat // weekly)
## Robin Hanson
(mkText "https://www.overcomingbias.com/feed" // rat // daily)
## Scott Alexander
(mkSubstack "astralcodexten" // rat // daily)
## Paul Christiano
(mkText "https://sideways-view.com/feed" // rat // infrequent)
## Sean Carroll
(mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent)
## mostly dating topics. not advice, or humor, but looking through a social lens
(mkText "https://putanumonit.com/feed" // rat // infrequent)
# CODE
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
];
images = [
(mkImg "https://www.smbc-comics.com/comic/rss" // humor // daily)
(mkImg "https://xkcd.com/atom.xml" // humor // daily)
(mkImg "https://pbfcomics.com/feed" // humor // infrequent)
# (mkImg "http://dilbert.com/feed" // humor // daily)
# ART
(mkImg "https://miniature-calendar.com/feed" // art // daily)
];
in
{
sane.feeds = texts ++ images ++ podcasts;
assertions = builtins.map
(p: {
assertion = p.format or "unknown" == "podcast";
message = ''${p.url} is not a podcast: ${p.format or "unknown"}'';
})
podcasts;
}

View File

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

View File

@@ -1,60 +0,0 @@
{ ... }:
{
# legacy servo users, some are inconvenient to migrate
sane.ids.dhcpcd.gid = 991;
sane.ids.dhcpcd.uid = 992;
sane.ids.gitea.gid = 993;
sane.ids.git.uid = 994;
sane.ids.jellyfin.gid = 994;
sane.ids.pleroma.gid = 995;
sane.ids.jellyfin.uid = 996;
sane.ids.acme.gid = 996;
sane.ids.pleroma.uid = 997;
sane.ids.acme.uid = 998;
# greetd (used by sway)
sane.ids.greeter.uid = 999;
sane.ids.greeter.gid = 999;
# new servo users
sane.ids.freshrss.uid = 2401;
sane.ids.freshrss.gid = 2401;
sane.ids.mediawiki.uid = 2402;
sane.ids.colin.uid = 1000;
sane.ids.guest.uid = 1100;
# found on all hosts
sane.ids.sshd.uid = 2001; # 997
sane.ids.sshd.gid = 2001; # 997
sane.ids.polkituser.gid = 2002; # 998
sane.ids.systemd-coredump.gid = 2003; # 996
sane.ids.nscd.uid = 2004;
sane.ids.nscd.gid = 2004;
sane.ids.systemd-oom.uid = 2005;
sane.ids.systemd-oom.gid = 2005;
# found on graphical hosts
sane.ids.nm-iodine.uid = 2101; # desko/moby/lappy
# found on desko host
# from services.usbmuxd
sane.ids.usbmux.uid = 2204;
sane.ids.usbmux.gid = 2204;
# originally found on moby host
# gnome core-shell
sane.ids.avahi.uid = 2304;
sane.ids.avahi.gid = 2304;
sane.ids.colord.uid = 2305;
sane.ids.colord.gid = 2305;
sane.ids.geoclue.uid = 2306;
sane.ids.geoclue.gid = 2306;
# gnome core-os-services
sane.ids.rtkit.uid = 2307;
sane.ids.rtkit.gid = 2307;
# phosh
sane.ids.feedbackd.gid = 2308;
}

View File

@@ -1,16 +0,0 @@
{ ... }:
{
# /etc/machine-id is a globally unique identifier used for:
# - systemd-networkd: DHCP lease renewal (instead of keying by the MAC address)
# - systemd-journald: to filter logs by host
# - chromium (potentially to track re-installations)
# - gdbus; system services that might upgrade to AF_LOCAL if both services can confirm they're on the same machine
# because of e.g. the chromium use, we *don't want* to persist this.
# however, `journalctl` won't show logs from previous boots unless the machine-ids match.
# so for now, generate something unique from the host ssh key.
# TODO: move this into modules?
system.activationScripts.machine-id = {
deps = [ "etc" ];
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
};
}

View File

@@ -1,43 +0,0 @@
{ config, lib, pkgs, ... }:
{
# if using router's DNS, these mappings will already exist.
# if using a different DNS provider (which servo does), then we need to explicity provide them.
# ugly hack. would be better to get servo to somehow use the router's DNS
networking.hosts = {
"192.168.0.5" = [ "servo" ];
"192.168.0.20" = [ "lappy" ];
"192.168.0.22" = [ "desko" ];
"192.168.0.48" = [ "moby" ];
};
# the default backend is "wpa_supplicant".
# wpa_supplicant reliably picks weak APs to connect to.
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
# iwd is an alternative that shouldn't have this problem
# docs:
# - <https://nixos.wiki/wiki/Iwd>
# - <https://iwd.wiki.kernel.org/networkmanager>
# - `man iwd.config` for global config
# - `man iwd.network` for per-SSID config
# use `iwctl` to control
networking.networkmanager.wifi.backend = "iwd";
networking.wireless.iwd.enable = true;
networking.wireless.iwd.settings = {
# auto-connect to a stronger network if signal drops below this value
# bedroom -> bedroom connection is -35 to -40 dBm
# bedroom -> living room connection is -60 dBm
General.RoamThreshold = "-52"; # default -70
General.RoamThreshold5G = "-52"; # default -76
};
sane.fs."/var/lib/iwd/.secrets.psk.stamp" = {
wantedBeforeBy = [ "iwd.service" ];
generated.acl.mode = "0600";
# XXX: install-iwd uses sed, but that's part of the default systemd unit path, it seems
generated.script.script = builtins.readFile ../../scripts/install-iwd + ''
touch "/var/lib/iwd/.secrets.psk.stamp"
'';
generated.script.scriptArgs = [ "/run/secrets/iwd" "/var/lib/iwd" ];
};
}

View File

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

View File

@@ -1,24 +0,0 @@
{ config, lib, sane-data, sane-lib, ... }:
{
sane.ssh.pubkeys =
let
# path is a DNS-style path like [ "org" "uninsane" "root" ]
keyNameForPath = path:
let
rev = lib.reverseList path;
name = builtins.head rev;
host = lib.concatStringsSep "." (builtins.tail rev);
in
"${name}@${host}";
# [{ path :: [String], value :: String }] for the keys we want to install
globalKeys = sane-lib.flattenAttrs sane-data.keys;
localKeys = sane-lib.flattenAttrs sane-data.keys.org.uninsane.local;
in lib.mkMerge (builtins.map
({ path, value }: {
"${keyNameForPath path}" = value;
})
(globalKeys ++ localKeys)
);
}

View File

@@ -1,132 +0,0 @@
{ config, pkgs, lib, sane-lib, ... }:
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
with lib;
let
cfg = config.sane.users;
fs = sane-lib.fs;
in
{
options = {
sane.users.guest.enable = mkOption {
default = false;
type = types.bool;
};
};
config = {
# Users are exactly these specified here;
# old ones will be deleted (from /etc/passwd, etc) upon upgrade.
users.mutableUsers = false;
# docs: https://nixpkgs-manual-sphinx-markedown-example.netlify.app/generated/options-db.xml.html#users-users
users.users.colin = {
# sets group to "users" (?)
isNormalUser = true;
home = "/home/colin";
createHome = true;
homeMode = "0700";
# i don't get exactly what this is, but nixos defaults to this non-deterministically
# in /var/lib/nixos/auto-subuid-map and i don't want that.
subUidRanges = [
{ startUid=100000; count=1; }
];
group = "users";
extraGroups = [
"wheel"
"nixbuild"
"networkmanager"
# phosh/mobile. XXX colin: unsure if necessary
"video"
"feedbackd"
"dialout" # required for modem access
];
# initial password is empty, in case anything goes wrong.
# if `colin-passwd` (a password hash) is successfully found/decrypted, that becomes the password at boot.
initialPassword = lib.mkDefault "";
passwordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
shell = pkgs.zsh;
# mount encrypted stuff at login
# some other nix pam users:
# - <https://github.com/g00pix/nixconf/blob/32c04f6fa843fed97639dd3f09e157668d3eea1f/profiles/sshfs.nix>
# - <https://github.com/lourkeur/distro/blob/11173454c6bb50f7ccab28cc2c757dca21446d1d/nixos/profiles/users/louis-full.nix>
# - <https://github.com/dnr/sample-nix-code/blob/03494480c1fae550c033aa54fd96aeb3827761c5/nixos/laptop.nix>
pamMount = let
priv = config.fileSystems."/home/colin/private";
in {
fstype = priv.fsType;
path = priv.device;
mountpoint = priv.mountPoint;
options = builtins.concatStringsSep "," priv.options;
};
};
security.pam.mount.enable = true;
# ensure ~ perms are known to sane.fs module.
# TODO: this is generic enough to be lifted up into sane.fs itself.
sane.fs."/home/colin".dir.acl = {
user = "colin";
group = config.users.users.colin.group;
mode = config.users.users.colin.homeMode;
};
sane.persist.home.plaintext = [
"archive"
"dev"
# TODO: records should be private
"records"
"ref"
"tmp"
"use"
"Music"
"Pictures"
"Videos"
".cargo"
".rustup"
];
# convenience
sane.fs."/home/colin/knowledge" = fs.wantedSymlinkTo "/home/colin/private/knowledge";
sane.fs."/home/colin/nixos" = fs.wantedSymlinkTo "/home/colin/dev/nixos";
sane.fs."/home/colin/Videos/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Videos";
sane.fs."/home/colin/Videos/servo-incomplete" = fs.wantedSymlinkTo "/mnt/servo-media/incomplete";
sane.fs."/home/colin/Music/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Music";
# used by password managers, e.g. unix `pass`
sane.fs."/home/colin/.password-store" = fs.wantedSymlinkTo "/home/colin/knowledge/secrets/accounts";
sane.persist.sys.plaintext = mkIf cfg.guest.enable [
# intentionally allow other users to write to the guest folder
{ directory = "/home/guest"; user = "guest"; group = "users"; mode = "0775"; }
];
users.users.guest = mkIf cfg.guest.enable {
isNormalUser = true;
home = "/home/guest";
subUidRanges = [
{ startUid=200000; count=1; }
];
group = "users";
initialPassword = lib.mkDefault "";
shell = pkgs.zsh;
openssh.authorizedKeys.keys = [
# TODO: insert pubkeys that should be allowed in
];
};
security.sudo = {
enable = true;
wheelNeedsPassword = false;
};
services.openssh = {
enable = true;
permitRootLogin = "no";
passwordAuthentication = false;
};
};
}

View File

@@ -1,66 +0,0 @@
{ config, lib, ... }:
# to add a new OVPN VPN:
# - generate a privkey `wg genkey`
# - add this key to `sops secrets/universal.yaml`
# - upload pubkey to OVPN.com
# - generate config @ OVPN.com
# - copy the Address, PublicKey, Endpoint from OVPN's config
# N.B.: maximum interface name in Linux is 15 characters.
let
def-ovpn = name: { endpoint, publicKey, address }: {
networking.wg-quick.interfaces."ovpnd-${name}" = {
inherit address;
privateKeyFile = config.sops.secrets."wg_ovpnd_${name}_privkey".path;
dns = [
"46.227.67.134"
"192.165.9.158"
];
peers = [
{
allowedIPs = [
"0.0.0.0/0"
"::/0"
];
inherit endpoint publicKey;
}
];
# to start: `systemctl start wg-quick-ovpnd-${name}`
autostart = false;
};
};
in lib.mkMerge [
(def-ovpn "us" {
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
address = [
"172.27.237.218/32"
"fd00:0000:1337:cafe:1111:1111:ab00:4c8f/128"
];
})
# NB: us-* share the same wg key and link-local addrs, but distinct public addresses
(def-ovpn "us-atl" {
endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
address = [
"172.21.182.178/32"
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
];
})
(def-ovpn "us-mi" {
endpoint = "vpn34.prd.miami.ovpn.com:9929";
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
address = [
"172.21.182.178/32"
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
];
})
(def-ovpn "ukr" {
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
address = [
"172.18.180.159/32"
"fd00:0000:1337:cafe:1111:1111:ec5c:add3/128"
];
})
]

View File

@@ -1,23 +0,0 @@
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
{ hostName, localSystem }:
{ ... }:
{
imports = [
./${hostName}
./common
];
networking.hostName = hostName;
nixpkgs.overlays = [
(next: prev: {
# for local != target we by default just emulate the target while building.
# provide a `pkgs.cross.<pkg>` alias that consumers can use instead of `pkgs.<foo>`
# to explicitly opt into non-emulated cross compilation for any specific package.
# this is most beneficial for large packages with few pre-requisites -- like Linux.
cross = next.crossFrom."${localSystem}";
})
];
}

View File

@@ -1,93 +0,0 @@
{ ... }:
{
sane.persist.root-on-tmpfs = true;
# 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";
fsType = "btrfs";
options = [
"compress=zstd"
"defaults"
];
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/6EE3-4171";
fsType = "vfat";
};
# slow, external storage (for archiving, etc)
fileSystems."/mnt/persist/ext" = {
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
fsType = "btrfs";
options = [
"compress=zstd"
"defaults"
];
};
sane.persist.stores."ext" = {
origin = "/mnt/persist/ext/persist";
storeDescription = "external HDD storage";
};
sane.fs."/mnt/persist/ext".mount = {};
sane.persist.sys.plaintext = [
# TODO: this is overly broad; only need media and share directories to be persisted
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
];
# make sure large media is stored to the HDD
sane.persist.sys.ext = [
{
user = "colin";
group = "users";
mode = "0777";
directory = "/var/lib/uninsane/media/Videos";
}
{
user = "colin";
group = "users";
mode = "0777";
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; }
# ];
# this can be a partition. create with:
# fdisk <dev>
# n
# <default partno>
# <start>
# <end>
# t
# <partno>
# 19 # set part type to Linux swap
# w # write changes
# mkswap -L swap <part>
# swapDevices = [
# {
# label = "swap";
# # TODO: randomEncryption.enable = true;
# }
# ];
}

View File

@@ -1,212 +0,0 @@
{ config, pkgs, ... }:
{
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;
# networking.firewall.enable = false;
networking.firewall.enable = true;
# this is needed to forward packets from the VPN to the host
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# 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"
# ];
# 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"
];
# 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 `wg0` interface.
# # i hope that from the root ns, these aren't visible.
# DNS=46.227.67.134%wg0 192.165.9.158%wg0
# 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-wg0`, 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.wg0 = 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"
];
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
# 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;
sops.secrets."wg_ovpns_privkey" = {
sopsFile = ../../secrets/servo.yaml;
};
# 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

@@ -1,31 +0,0 @@
{ config, lib, pkgs, ... }:
# using manual ddns now
lib.mkIf false
{
systemd.services.ddns-afraid = {
description = "update dynamic DNS entries for freedns.afraid.org";
serviceConfig = {
EnvironmentFile = config.sops.secrets.ddns_afraid.path;
# TODO: ProtectSystem = "strict";
# TODO: ProtectHome = "full";
# TODO: PrivateTmp = true;
};
script = let
curl = "${pkgs.curl}/bin/curl -4";
in ''
${curl} "https://freedns.afraid.org/dynamic/update.php?$AFRAID_KEY"
'';
};
systemd.timers.ddns-afraid = {
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnStartupSec = "2min";
OnUnitActiveSec = "10min";
};
};
sops.secrets."ddns_afraid" = {
sopsFile = ../../../secrets/servo.yaml;
};
}

View File

@@ -1,393 +0,0 @@
# docs:
# - <https://docs.ejabberd.im/admin/configuration/basic>
# example configs:
# - <https://github.com/vkleen/machines/blob/138a2586ce185d7cf201d4e1fe898c83c4af52eb/hosts/europium/ejabberd.nix>
# - <https://github.com/Mic92/stockholm/blob/675ef0088624c9de1cb531f318446316884a9d3d/tv/3modules/ejabberd/default.nix>
# - <https://github.com/buffet/tararice/blob/master/programs/ejabberd.nix>
# - enables STUN and TURN
# - only over UDP 3478, not firewall-forwarding any TURN port range
# - uses stun_disco module (but with no options)
# - <https://github.com/leo60228/dotfiles/blob/39b3abba3009bdc31413d4757ca2f882a33eec8b/files/ejabberd.yml>
# - <https://github.com/Mic92/dotfiles/blob/ddf0f4821f554f7667fc803344657367c55fb9e6/nixos/eve/modules/ejabberd.nix>
# - <nixpkgs:nixos/tests/xmpp/ejabberd.nix>
# - 2013: <https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example>
#
# compliance tests:
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
{ config, lib, pkgs, ... }:
# XXX: avatar support works in MUCs but not DMs
# lib.mkIf false
{
sane.persist.sys.plaintext = [
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
];
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
users.users.ejabberd.extraGroups = [ "nginx" ];
security.acme.certs."uninsane.org".extraDomainNames = [
"xmpp.uninsane.org"
"muc.xmpp.uninsane.org"
"pubsub.xmpp.uninsane.org"
"upload.xmpp.uninsane.org"
"vjid.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."muc.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."pubsub.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."upload.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."vjid.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
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" = "%NATIVE%";
CNAME."muc.xmpp" = "xmpp";
CNAME."pubsub.xmpp" = "xmpp";
CNAME."upload.xmpp" = "xmpp";
CNAME."vjid.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 muc.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.muc.xmpp" = "3 50 5223 xmpp";
SRV."_xmpps-server._tcp.muc.xmpp" = "3 50 5270 xmpp";
SRV."_xmpp-client._tcp.muc.xmpp" = "5 50 5222 xmpp";
SRV."_xmpp-server._tcp.muc.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";
SRV."_stun._udp" = "5 50 3478 xmpp";
SRV."_stun._tcp" = "5 50 3478 xmpp";
SRV."_stuns._tcp" = "5 50 5349 xmpp";
SRV."_turn._udp" = "5 50 3478 xmpp";
SRV."_turn._tcp" = "5 50 3478 xmpp";
SRV."_turns._tcp" = "5 50 5349 xmpp";
};
# TODO: allocate UIDs/GIDs ?
services.ejabberd.enable = true;
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
systemd.services.ejabberd.preStart = let
config-in = pkgs.writeTextFile {
name = "ejabberd.yaml.in";
text = ''
hosts:
- uninsane.org
# none | emergency | alert | critical | error | warning | notice | info | debug
loglevel: debug
# loglevel: info
# loglevel: notice
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
pam_userinfotype: jid
acl:
admin:
user:
- "colin@uninsane.org"
local:
user_regexp: ""
loopback:
ip:
- 127.0.0.0/8
- ::1/128
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
# 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
# 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
# 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/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
'';
sane.services.dyn-dns.restartOnChange = [ "ejabberd.service" ];
}

View File

@@ -1,67 +0,0 @@
{ config, lib, ... }:
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
# else it's too spammy
lib.mkIf false
{
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"; }
];
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
'';
};
# locations."/web/" = {
# proxyPass = "http://127.0.0.1:8096/web/index.html";
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Protocol $scheme;
# proxy_set_header X-Forwarded-Host $http_host;
# '';
# };
locations."/socket" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jelly" = "native";
services.jellyfin.enable = true;
}

View File

@@ -1,17 +0,0 @@
{ ... }:
{
sane.services.kiwix-serve = {
enable = true;
port = 8013;
zimPaths = [ "/var/lib/uninsane/www-archive/wikipedia_en_all_maxi_2022-05.zim" ];
};
services.nginx.virtualHosts."w.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/".proxyPass = "http://127.0.0.1:8013";
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."w" = "native";
}

View File

@@ -1,29 +0,0 @@
{ ... }:
{
sane.persist.sys.plaintext = [
# TODO: we don't have a static user allocated for navidrome!
# the chown would happen too early for us to set static perms
"/var/lib/private/navidrome"
# { user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
];
services.navidrome.enable = true;
services.navidrome.settings = {
# docs: https://www.navidrome.org/docs/usage/configuration-options/
Address = "127.0.0.1";
Port = 4533;
MusicFolder = "/var/lib/uninsane/media/Music";
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
AutoImportPlaylists = false;
ScanSchedule = "@every 1h";
};
services.nginx.virtualHosts."music.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/".proxyPass = "http://127.0.0.1:4533";
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."music" = "native";
}

View File

@@ -1,166 +0,0 @@
# docs: https://nixos.wiki/wiki/Nginx
{ config, lib, pkgs, ... }:
let
# make the logs for this host "public" so that they show up in e.g. metrics
publog = vhost: lib.attrsets.unionOfDisjoint vhost {
extraConfig = (vhost.extraConfig or "") + ''
access_log /var/log/nginx/public.log vcombined;
'';
};
# kTLS = true; # in-kernel TLS for better perf
in
{
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.nginx.enable = true;
services.nginx.appendConfig = ''
# use 1 process per core.
# may want to increase worker_connections too, but `ulimit -n` must be increased first.
worker_processes auto;
'';
# this is the standard `combined` log format, with the addition of $host
# so that we have the virtualHost in the log.
# KEEP IN SYNC WITH GOACCESS
# goaccess calls this VCOMBINED:
# - <https://gist.github.com/jyap808/10570005>
services.nginx.commonHttpConfig = ''
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
access_log /var/log/nginx/private.log vcombined;
'';
# sets gzip_comp_level = 5
services.nginx.recommendedGzipSettings = true;
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
# - doesn't seem to, actually: <https://www.ssllabs.com/ssltest/analyze.html?d=uninsane.org>
# caches TLS sessions for 10m
services.nginx.recommendedTlsSettings = true;
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
services.nginx.recommendedOptimisation = true;
# web blog/personal site
services.nginx.virtualHosts."uninsane.org" = publog {
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
# a lot of places hardcode https://uninsane.org,
# and then when we mix http + non-https, we get CORS violations
# and things don't look right. so force SSL.
forceSSL = true;
enableACME = true;
# inherit kTLS;
# for OCSP stapling
sslTrustedCertificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
# yes, nginx does not strip the prefix when evaluating against the root.
locations."/share".root = "/var/lib/uninsane/root";
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
locations."= /.well-known/matrix/server".extraConfig =
let
# use 443 instead of the default 8448 port to unite
# the client-server and server-server port for simplicity
server = { "m.server" = "matrix.uninsane.org:443"; };
in ''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
locations."= /.well-known/matrix/client".extraConfig =
let
client = {
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
# ACAO required to allow element-web on any URL to request this json file
in ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
# so hack around that.
locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008";
};
locations."/_synapse" = {
proxyPass = "http://127.0.0.1:8008";
};
# allow ActivityPub clients to discover how to reach @user@uninsane.org
# 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;
# };
};
# serve any site not listed above, if it's static.
# because we define it dynamically, SSL isn't trivial. support only http
# documented <https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name>
services.nginx.virtualHosts."~^(?<domain>.+)$" = {
default = true;
addSSL = true;
enableACME = false;
sslCertificate = "/var/www/certs/wildcard/cert.pem";
sslCertificateKey = "/var/www/certs/wildcard/key.pem";
# sslCertificate = "/var/lib/acme/.minica/cert.pem";
# sslCertificateKey = "/var/lib/acme/.minica/key.pem";
# serverName = null;
locations."/" = {
# somehow this doesn't escape -- i get error 400 if i:
# curl 'http://..' --resolve '..:80:127.0.0.1'
root = "/var/www/sites/$domain";
# tryFiles = "$domain/$uri $domain/$uri/ =404";
};
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin.acme@uninsane.org";
sane.persist.sys.plaintext = [
# TODO: mode?
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
];
# let's encrypt default chain looks like:
# - End-entity certificate ← R3 ← ISRG Root X1 ← DST Root CA X3
# - <https://community.letsencrypt.org/t/production-chain-changes/150739>
# DST Root CA X3 expired in 2021 (?)
# the alternative chain is:
# - End-entity certificate ← R3 ← ISRG Root X1 (self-signed)
# using this alternative chain grants more compatibility for services like ejabberd
# but might decrease compatibility with very old clients that don't get updates (e.g. old android, iphone <= 4).
# security.acme.defaults.extraLegoFlags = [
security.acme.certs."uninsane.org" = rec {
# ISRG Root X1 results in lets encrypt sending the same chain as default,
# just without the final ISRG Root X1 ← DST Root CA X3 link.
# i.e. we could alternative clip the last item and achieve the exact same thing.
extraLegoRunFlags = [
"--preferred-chain" "ISRG Root X1"
];
extraLegoRenewFlags = extraLegoRunFlags;
};
# TODO: alternatively, we could clip the last cert IF it's expired,
# optionally outputting that to a new cert file.
# security.acme.defaults.postRun = "";
# create a self-signed SSL certificate for use with literally any domain.
# browsers will reject this, but proxies and local testing tools can be configured
# to accept it.
system.activationScripts.generate-x509-self-signed.text = ''
mkdir -p /var/www/certs/wildcard
test -f /var/www/certs/wildcard/key.pem || ${pkgs.openssl}/bin/openssl \
req -x509 -newkey rsa:4096 \
-keyout /var/www/certs/wildcard/key.pem \
-out /var/www/certs/wildcard/cert.pem \
-sha256 -nodes -days 3650 \
-addext 'subjectAltName=DNS:*' \
-subj '/CN=self-signed'
chmod 640 /var/www/certs/wildcard/{key,cert}.pem
chown root:nginx /var/www/certs/wildcard /var/www/certs/wildcard/{key,cert}.pem
'';
}

View File

@@ -1,21 +0,0 @@
{ config, ... }:
{
services.nginx.virtualHosts."nixcache.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# serverAliases = [ "nixcache" ];
locations."/".extraConfig = ''
proxy_pass http://localhost:${toString config.services.nix-serve.port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
sane.services.nixserve.enable = true;
sane.services.nixserve.sopsFile = ../../../secrets/servo.yaml;
}

View File

@@ -1,64 +0,0 @@
# 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,66 +0,0 @@
{ config, pkgs, ... }:
{
sane.services.trust-dns.enable = 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.zones."uninsane.org".TTL = 900;
# SOA record structure: <https://en.wikipedia.org/wiki/SOA_record#Structure>
# SOA MNAME RNAME (... rest)
# MNAME = Master name server for this zone. this is where update requests should be sent.
# RNAME = admin contact (encoded email address)
# Serial = YYYYMMDDNN, where N is incremented every time this file changes, to trigger secondary NS to re-fetch it.
# 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.services.trust-dns.zones."uninsane.org".inet = {
SOA."@" = ''
ns1.uninsane.org. admin-dns.uninsane.org. (
2022122101 ; Serial
4h ; Refresh
30m ; Retry
7d ; Expire
5m) ; Negative response TTL
'';
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" = "%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."
"ns3.uninsane.org."
];
};
sane.services.trust-dns.zones."uninsane.org".file =
"/var/lib/trust-dns/uninsane.org.zone";
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.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
}

View File

@@ -1,31 +0,0 @@
# docs: <https://nixos.wiki/wiki/MediaWiki>
{ config, lib, ... }:
# XXX: working to host wikipedia with kiwix instead of mediawiki
# mediawiki does more than i need and isn't obviously superior in any way
# except that the dumps are more frequent/up-to-date.
lib.mkIf false
{
sops.secrets."mediawiki_pw" = {
owner = config.users.users.mediawiki.name;
sopsFile = ../../../secrets/servo.yaml;
};
services.mediawiki.enable = true;
services.mediawiki.name = "Uninsane Wiki";
services.mediawiki.passwordFile = config.sops.secrets.mediawiki_pw.path;
services.mediawiki.extraConfig = ''
# Disable anonymous editing
$wgGroupPermissions['*']['edit'] = false;
'';
services.mediawiki.virtualHost.listen = [
{
ip = "127.0.0.1";
port = 8013;
ssl = false;
}
];
services.mediawiki.virtualHost.hostName = "w.uninsane.org";
services.mediawiki.virtualHost.adminAddr = "admin+mediawiki@uninsane.org";
# services.mediawiki.extensions = TODO: wikipedia sync extension?
}

View File

@@ -4,28 +4,25 @@
./fs.nix
];
# sane.packages.enableDevPkgs = true;
sane.gui.sway.enable = true;
sane.services.duplicity.enable = true;
sane.services.nixserve.enable = true;
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
sane.persist.enable = true;
sane.impermanence.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
# needed to use libimobiledevice/ifuse, for iphone sync
services.usbmuxd.enable = true;
users.users.usbmux.uid = config.sane.allocations.usbmux-uid;
users.groups.usbmux.gid = config.sane.allocations.usbmux-gid;
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;
# default config: https://man.archlinux.org/man/snapper-configs.5
# defaults to something like:
# - hourly snapshots
@@ -50,7 +47,7 @@
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.persist.home.plaintext = [
sane.impermanence.home-dirs = [
".steam"
".local/share/Steam"
];

View File

@@ -1,7 +1,16 @@
{ ... }:
{
sane.persist.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp for building large nix things.
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
fileSystems."/tmp" = {

11
machines/instantiate.nix Normal file
View File

@@ -0,0 +1,11 @@
# trampoline from flake.nix into the specific machine definition, while doing a tiny bit of common setup
hostName: { ... }: {
imports = [
./${hostName}
];
networking.hostName = hostName;
nixpkgs.config.allowUnfree = true;
}

View File

@@ -4,11 +4,9 @@
./fs.nix
];
# sane.packages.enableDevPkgs = true;
# sane.users.guest.enable = true;
sane.gui.sway.enable = true;
sane.persist.enable = true;
sane.impermanence.enable = true;
sane.nixcache.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];

View File

@@ -1,7 +1,16 @@
{ ... }:
{
sane.persist.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp of default size (half RAM) for building large nix things
fileSystems."/tmp" = {
device = "none";

View File

@@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, mobile-nixos, ... }:
{
imports = [
./firmware.nix
@@ -24,26 +24,22 @@
};
# usability compromises
sane.web-browser.persistCache = "private";
sane.web-browser.persistData = "private";
sane.persist.home.plaintext = [
".config/pulse" # persist pulseaudio volume
sane.impermanence.home-dirs = [
".librewolf"
];
# sane.packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
sane.packages.extraUserPkgs = [
# sane.home-packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
sane.home-manager.extraPackages = [
pkgs.plasma5Packages.konsole # terminal
];
sane.nixcache.enable = true;
sane.persist.enable = true;
sane.impermanence.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;
boot.loader.generic-extlinux-compatible.configurationLimit = 10;
# mobile.bootloader.enable = false;
# mobile.boot.stage-1.enable = false;
# boot.initrd.systemd.enable = false;

View File

@@ -1,7 +1,17 @@
{ ... }:
{
sane.persist.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/1f1271f8-53ce-4081-8a29-60a4a6b5d6f9";
fsType = "btrfs";

View File

@@ -125,9 +125,6 @@ in
# 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;
})
))
];

View File

@@ -8,6 +8,9 @@
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
users.users.dhcpcd.uid = config.sane.allocations.dhcpcd-uid;
users.groups.dhcpcd.gid = config.sane.allocations.dhcpcd-gid;
# docs: https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion
system.stateVersion = "21.05";
}

View File

@@ -3,22 +3,27 @@
{
imports = [
./fs.nix
./hardware.nix
./net.nix
./users.nix
./services
];
sane.packages.extraUserPkgs = [
sane.home-manager.extraPackages = [
# for administering services
pkgs.matrix-synapse
pkgs.freshrss
pkgs.goaccess
];
sane.persist.enable = true;
sane.services.dyn-dns.enable = true;
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
sane.impermanence.enable = true;
sane.services.duplicity.enable = true;
sane.services.nixserve.enable = true;
# TODO: look into the EFI stuff
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.image.extraBootFiles = [ pkgs.bootpart-u-boot-rpi-aarch64 ];
sops.secrets.duplicity_passphrase = {
sopsFile = ../../secrets/servo.yaml;
@@ -27,7 +32,7 @@
# both transmission and ipfs try to set different net defaults.
# we just use the most aggressive of the two here:
boot.kernel.sysctl = {
"net.core.rmem_max" = 4194304; # 4MB
"net.core.rmem_max" = "4194304"; # 4MB
};
# This value determines the NixOS release from which the default
@@ -36,6 +41,6 @@
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "21.11";
system.stateVersion = "21.11"; # Did you read the comment?
}

69
machines/servo/fs.nix Normal file
View File

@@ -0,0 +1,69 @@
{ ... }:
{
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp for building large nix things
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"size=40G"
"mode=777"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
fsType = "btrfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/31D3-40CB";
fsType = "vfat";
};
# fileSystems."/var/lib/pleroma" = {
# device = "/opt/pleroma";
# options = [ "bind" ];
# };
# 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; }
# ];
# this can be a partition. create with:
# fdisk <dev>
# n
# <default partno>
# <start>
# <end>
# t
# <partno>
# 19 # set part type to Linux swap
# w # write changes
# mkswap -L swap <part>
swapDevices = [
{
label = "swap";
# TODO: randomEncryption.enable = true;
}
];
}

View File

@@ -0,0 +1,75 @@
# this file originates from nixos-generate-config
# but has been heavily modified
{ pkgs, ... }:
{
# i changed this becuse linux 5.10 didn't have rpi-400 device tree blob.
# nixos-22.05 linux 5.15 DOES have these now.
# it should be possible to remove this if desired, but i'm not sure how the rpi-specific kernel differs.
# see: https://github.com/raspberrypi/linux
boot.kernelPackages = pkgs.linuxPackages_rpi4;
# raspberryPi boot loader creates extlinux.conf.
# otherwise, enable the generic-extlinux-compatible loader below.
# note: THESE ARE MUTUALLY EXCLUSIVE. generic-extlinux-compatible causes uboot to not be built
boot.initrd.availableKernelModules = [
"bcm2711_thermal"
"bcm_phy_lib"
"brcmfmac"
"brcmutil"
"broadcom"
"clk_raspberrypi"
"drm" # Direct Render Manager
"enclosure" # SCSI ?
"fuse"
"mdio_bcm_unimac"
"pcie_brcmstb"
"raspberrypi_cpufreq"
"raspberrypi_hwmon"
"ses" # SCSI Enclosure Services
"uas" # USB attached storage
"uio" # userspace IO
"uio_pdrv_genirq"
"xhci_pci"
"xhci_pci_renesas"
];
# boot.initrd.compressor = "gzip"; # defaults to zstd
# ondemand power scaling keeps the cpu at low frequency when idle, and sets to max frequency
# when load is detected. (v.s. the "performance" default, which always uses the max frequency)
powerManagement.cpuFreqGovernor = "ondemand";
# XXX colin: this allows one to `systemctl halt` and then not remove power until the HDD has spun down.
# however, it doesn't work with reboot because systemd will spin the drive up again to read its reboot bin.
# a better solution would be to put the drive behind a powered USB hub (or get a SSD).
# systemd.services.diskguard = {
# description = "Safely power off spinning media";
# before = [ "shutdown.target" ];
# wantedBy = [ "sysinit.target" ];
# # old (creates dep loop, but works)
# # before = [ "systemd-remount-fs.service" "shutdown.target" ];
# # wantedBy = [ "systemd-remount-fs.service" ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = "${pkgs.coreutils}/bin/true";
# ExecStop = with pkgs; writeScript "diskguard" ''
# #!${bash}/bin/bash
# if ${procps}/bin/pgrep nixos-rebuild ;
# then
# exit 0 # don't halt drives unless we're actually shutting down. maybe better way to do this (check script args?)
# fi
# # ${coreutils}/bin/sync
# # ${util-linux}/bin/mount -o remount,ro /nix/store
# # ${util-linux}/bin/mount -o remount,ro /
# # -S 1 retracts the spindle after 5 seconds of idle
# # -B 1 spins down the drive after <vendor specific duration>
# ${hdparm}/sbin/hdparm -S 1 -B 1 /dev/sda
# # TODO: monitor smartmonctl until disk is idle? or try hdparm -Y
# # ${coreutils}/bin/sleep 20
# # exec ${util-linux}/bin/umount --all -t ext4,vfat,ext2
# '';
# };
# };
}

139
machines/servo/net.nix Normal file
View File

@@ -0,0 +1,139 @@
{ config, pkgs, ... }:
{
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;
# networking.firewall.enable = false;
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [
25 # SMTP
80 # HTTP
143 # IMAP
443 # HTTPS
465 # SMTPS
587 # SMTPS/submission
993 # IMAPS
4001 # IPFS
];
networking.firewall.allowedUDPPorts = [
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
4001 # IPFS
];
# we need to use externally-visible nameservers in order for VPNs to be able to resolve hosts.
networking.nameservers = [
"1.1.1.1"
"9.9.9.9"
];
# OVPN CONFIG (https://www.ovpn.com):
# DOCS: https://nixos.wiki/wiki/WireGuard
networking.wireguard.enable = true;
networking.wireguard.interfaces.wg0 = {
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
# note: without the namespace, you'll need to add a specific route through eth0 for the peer (185.157.162.178/32)
interfaceNamespace = "ovpns";
preSetup = "${pkgs.iproute2}/bin/ip netns add ovpns || true";
postShutdown = "${pkgs.iproute2}/bin/ip netns delete ovpns";
ips = [
"185.157.162.178/32"
];
peers = [
{
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
allowedIPs = [ "0.0.0.0/0" ];
# nixOS says this is important for keeping NATs active
persistentKeepalive = 25;
}
];
};
systemd.services.wg0veth = {
description = "veth pair to allow communication between host and wg0 netns";
after = [ "wireguard-wg0.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = with pkgs; writeScript "wg0veth-start" ''
#!${bash}/bin/bash
# create veth pair
${iproute2}/bin/ip link add ovpns-veth-a type veth peer name ovpns-veth-b
${iproute2}/bin/ip addr add 10.0.1.5/24 dev ovpns-veth-a
${iproute2}/bin/ip link set ovpns-veth-a up
# mv veth-b into the ovpns namespace
${iproute2}/bin/ip link set ovpns-veth-b netns ovpns
${iproute2}/bin/ip -n ovpns addr add 10.0.1.6/24 dev ovpns-veth-b
${iproute2}/bin/ip -n ovpns link set ovpns-veth-b up
# forward HTTP traffic, which we need for letsencrypt to work
${iproute2}/bin/ip netns exec ovpns ${socat}/bin/socat TCP4-LISTEN:80,reuseaddr,fork,su=nobody TCP4:10.0.1.5:80 &
'';
ExecStop = with pkgs; writeScript "wg0veth-stop" ''
#!${bash}/bin/bash
${iproute2}/bin/ip -n wg0 link del ovpns-veth-b
${iproute2}/bin/ip link del ovpns-veth-a
'';
};
};
sops.secrets."wg_ovpns_privkey" = {
sopsFile = ../../secrets/servo.yaml;
};
# 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

@@ -1,7 +1,5 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
# we use manual DDNS now
lib.mkIf false
{
systemd.services.ddns-he = {
description = "update dynamic DNS entries for HurricaneElectric";

View File

@@ -1,26 +1,19 @@
{ ... }:
{
imports = [
./ddns-afraid.nix
./ddns-he.nix
./ejabberd.nix
./freshrss.nix
./gitea.nix
./goaccess.nix
./ipfs.nix
./jackett.nix
./jellyfin.nix
./kiwix-serve.nix
./matrix
./navidrome.nix
./nixserve.nix
./nginx.nix
./pleroma.nix
./postfix.nix
./postgres.nix
./prosody.nix
./transmission.nix
./trust-dns.nix
./wikipedia.nix
];
}

View File

@@ -9,17 +9,19 @@
# $ sudo -u freshrss -g freshrss FRESHRSS_DATA_PATH=/var/lib/freshrss ./result/cli/export-opml-for-user.php --user admin
# ```
{ config, lib, pkgs, sane-lib, ... }:
{ config, lib, pkgs, ... }:
{
sops.secrets.freshrss_passwd = {
sopsFile = ../../../secrets/servo.yaml;
owner = config.users.users.freshrss.name;
mode = "0400";
mode = "400";
};
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
];
users.users.freshrss.uid = config.sane.allocations.freshrss-uid;
users.groups.freshrss.gid = config.sane.allocations.freshrss-gid;
services.freshrss.enable = true;
services.freshrss.baseUrl = "https://rss.uninsane.org";
services.freshrss.virtualHost = "rss.uninsane.org";
@@ -27,11 +29,9 @@
systemd.services.freshrss-import-feeds =
let
feeds = sane-lib.feeds;
fresh = config.systemd.services.freshrss-config;
all-feeds = config.sane.feeds;
wanted-feeds = feeds.filterByFormat ["text" "image"] all-feeds;
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml wanted-feeds);
feeds = import ../../../modules/universal/home-manager/feeds.nix { inherit lib; };
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all);
in {
inherit (fresh) wantedBy environment;
serviceConfig = {
@@ -45,17 +45,4 @@
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
'';
};
# the default ("*:0/5") is to run every 5 minutes.
# `systemctl list-timers` to show
systemd.services.freshrss-updater.startAt = lib.mkForce "*:3/30";
services.nginx.virtualHosts."rss.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# the routing is handled by services.freshrss.virtualHost
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."rss" = "native";
}

View File

@@ -1,10 +1,11 @@
{ config, pkgs, lib, ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
];
users.groups.gitea.gid = config.sane.allocations.gitea-gid;
services.gitea.enable = true;
services.gitea.user = "git"; # default is 'gitea'
services.gitea.database.type = "postgres";
@@ -71,18 +72,4 @@
"/var/lib/gitea"
];
};
# hosted git (web view and for `git <cmd>` use
# TODO: enable publog?
services.nginx.virtualHosts."git.uninsane.org" = {
forceSSL = true; # gitea complains if served over a different protocol than its config file says
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
}

View File

@@ -14,7 +14,6 @@
-f /var/log/nginx/public.log \
--log-format=VCOMBINED \
--real-time-html \
--html-refresh=30 \
--no-query-string \
--anonymize-ip \
--ignore-panel=HOSTS \
@@ -25,7 +24,6 @@
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Type = "simple";
Restart = "on-failure";
RestartSec = "10s";
# hardening
WorkingDirectory = "/tmp";
@@ -43,26 +41,4 @@
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
# server statistics
services.nginx.virtualHosts."sink.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
root = "/var/lib/uninsane/sink";
locations."/ws" = {
proxyPass = "http://127.0.0.1:7890";
# XXX not sure how much of this is necessary
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 7d;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."sink" = "native";
}

View File

@@ -6,50 +6,26 @@
# - number of open peer connections:
# - sudo -u ipfs -g ipfs ipfs -c /var/lib/ipfs/ swarm peers | wc -l
{ lib, ... }:
lib.mkIf false # i don't actively use ipfs anymore
{ ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
];
networking.firewall.allowedTCPPorts = [ 4001 ];
networking.firewall.allowedUDPPorts = [ 4001 ];
services.nginx.virtualHosts."ipfs.uninsane.org" = {
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
# ideally we'd disable ssl entirely, but some places assume it?
addSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Ipfs-Gateway-Prefix "";
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."ipfs" = "native";
# services.ipfs.enable = true;
services.kubo.localDiscovery = true;
services.kubo.settings = {
services.kubo.swarmAddress = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
# "/ip4/0.0.0.0/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
"/ip4/0.0.0.0/udp/4001/quic"
];
services.kubo.extraConfig = {
Addresses = {
Announce = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
];
Swarm = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
# "/ip4/0.0.0.0/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
"/ip4/0.0.0.0/udp/4001/quic"
];
};
Gateway = {
# the gateway can only be used to serve content already replicated on this host

View File

@@ -1,32 +1,18 @@
{ ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
];
services.jackett.enable = true;
systemd.services.jackett.after = [ "wireguard-wg0.service" ];
systemd.services.jackett.partOf = [ "wireguard-wg0.service" ];
systemd.services.jackett.after = ["wg0veth.service"];
systemd.services.jackett.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
# patch jackett to listen on the public interfaces
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
};
# jackett torrent search
services.nginx.virtualHosts."jackett.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jackett" = "native";
}

View File

@@ -0,0 +1,14 @@
{ config, ... }:
{
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
];
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
# else it's too spammy
# services.jellyfin.enable = true;
}

View File

@@ -1,6 +1,6 @@
# docs: https://nixos.wiki/wiki/Matrix
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
{
imports = [
@@ -8,7 +8,7 @@
# ./irc.nix
];
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
];
services.matrix-synapse.enable = true;
@@ -77,55 +77,6 @@
# create a token with limited uses:
# curl -d '{ "uses_allowed": 1 }' --header "Authorization: Bearer <my_token>" localhost:8008/_synapse/admin/v1/registration_tokens/new
# matrix chat server
# TODO: was `publog`
services.nginx.virtualHosts."matrix.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# TODO colin: replace this with something helpful to the viewer
# locations."/".extraConfig = ''
# return 404;
# '';
locations."/" = {
proxyPass = "http://127.0.0.1:8008";
};
# redirect browsers to the web client.
# i don't think native matrix clients ever fetch the root.
# ideally this would be put behind some user-agent test though.
locations."= /" = {
return = "301 https://web.matrix.uninsane.org";
};
# locations."/_matrix" = {
# proxyPass = "http://127.0.0.1:8008";
# };
};
# matrix web client
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://matrix.uninsane.org";
"server_name" = "uninsane.org";
};
};
};
};
sane.services.trust-dns.zones."uninsane.org".inet = {
CNAME."matrix" = "native";
CNAME."web.matrix" = "native";
};
sops.secrets.matrix_synapse_secrets = {
sopsFile = ../../../../secrets/servo.yaml;

View File

@@ -1,6 +1,6 @@
{ lib, ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
];

View File

@@ -1,7 +1,7 @@
{ config, lib, ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode?
# user and group are both "matrix-appservice-irc"
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }

View File

@@ -0,0 +1,17 @@
{ ... }:
{
sane.impermanence.service-dirs = [
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
];
services.navidrome.enable = true;
services.navidrome.settings = {
# docs: https://www.navidrome.org/docs/usage/configuration-options/
Address = "127.0.0.1";
Port = 4533;
MusicFolder = "/var/lib/uninsane/media/Music";
CovertArtPriority = "*.jpg, *.JPG, *.png, *.PNG, embedded";
AutoImportPlaylists = false;
ScanSchedule = "@every 1h";
};
}

View File

@@ -0,0 +1,320 @@
# docs: https://nixos.wiki/wiki/Nginx
{ config, pkgs, ... }:
let
# make the logs for this host "public" so that they show up in e.g. metrics
publog = vhost: vhost // {
extraConfig = (vhost.extraConfig or "") + ''
access_log /var/log/nginx/public.log vcombined;
'';
};
in
{
services.nginx.enable = true;
# this is the standard `combined` log format, with the addition of $host
# so that we have the virtualHost in the log.
# KEEP IN SYNC WITH GOACCESS
# goaccess calls this VCOMBINED:
# - <https://gist.github.com/jyap808/10570005>
services.nginx.commonHttpConfig = ''
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
access_log /var/log/nginx/private.log vcombined;
'';
# web blog/personal site
services.nginx.virtualHosts."uninsane.org" = publog {
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
# a lot of places hardcode https://uninsane.org,
# and then when we mix http + non-https, we get CORS violations
# and things don't look right. so force SSL.
forceSSL = true;
enableACME = true;
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
# yes, nginx does not strip the prefix when evaluating against the root.
locations."/share".root = "/var/lib/uninsane/root";
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
locations."= /.well-known/matrix/server".extraConfig =
let
# use 443 instead of the default 8448 port to unite
# the client-server and server-server port for simplicity
server = { "m.server" = "matrix.uninsane.org:443"; };
in ''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
locations."= /.well-known/matrix/client".extraConfig =
let
client = {
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
# ACAO required to allow element-web on any URL to request this json file
in ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
# so hack around that.
locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008";
};
locations."/_synapse" = {
proxyPass = "http://127.0.0.1:8008";
};
# allow ActivityPub clients to discover how to reach @user@uninsane.org
# 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;
# };
};
# server statistics
services.nginx.virtualHosts."sink.uninsane.org" = {
addSSL = true;
enableACME = true;
root = "/var/lib/uninsane/sink";
locations."/ws" = {
proxyPass = "http://127.0.0.1:7890";
# XXX not sure how much of this is necessary
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 7d;
'';
};
};
# Pleroma server and web interface
services.nginx.virtualHosts."fed.uninsane.org" = publog {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:4000";
# 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
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
if ($request_method = OPTIONS) {
return 204;
}
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
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;
# 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;
client_max_body_size 16m;
'';
};
};
# transmission web client
services.nginx.virtualHosts."bt.uninsane.org" = {
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
forceSSL = true;
enableACME = true;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9091";
proxyPass = "http://10.0.1.6:9091";
};
};
# jackett torrent search
services.nginx.virtualHosts."jackett.uninsane.org" = {
forceSSL = true;
enableACME = true;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
};
};
# matrix chat server
services.nginx.virtualHosts."matrix.uninsane.org" = publog {
addSSL = true;
enableACME = true;
# TODO colin: replace this with something helpful to the viewer
# locations."/".extraConfig = ''
# return 404;
# '';
locations."/" = {
proxyPass = "http://127.0.0.1:8008";
};
# redirect browsers to the web client.
# i don't think native matrix clients ever fetch the root.
# ideally this would be put behind some user-agent test though.
locations."= /" = {
return = "301 https://web.matrix.uninsane.org";
};
# locations."/_matrix" = {
# proxyPass = "http://127.0.0.1:8008";
# };
};
# matrix web client
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
forceSSL = true;
enableACME = true;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://matrix.uninsane.org";
"server_name" = "uninsane.org";
};
};
};
};
# hosted git (web view and for `git <cmd>` use
services.nginx.virtualHosts."git.uninsane.org" = publog {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
'';
};
# locations."/web/" = {
# proxyPass = "http://127.0.0.1:8096/web/index.html";
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Protocol $scheme;
# proxy_set_header X-Forwarded-Host $http_host;
# '';
# };
locations."/socket" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
'';
};
};
services.nginx.virtualHosts."music.uninsane.org" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://127.0.0.1:4533";
};
services.nginx.virtualHosts."rss.uninsane.org" = {
addSSL = true;
enableACME = true;
# the routing is handled by freshrss.nix
};
services.nginx.virtualHosts."ipfs.uninsane.org" = {
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
# ideally we'd disable ssl entirely, but some places assume it?
addSSL = true;
enableACME = true;
default = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Ipfs-Gateway-Prefix "";
'';
};
};
# exists only to manage certs for dovecot
services.nginx.virtualHosts."imap.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
# exists only to manage certs for Postfix
services.nginx.virtualHosts."mx.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
services.nginx.virtualHosts."nixcache.uninsane.org" = {
addSSL = true;
enableACME = true;
# serverAliases = [ "nixcache" ];
locations."/".extraConfig = ''
proxy_pass http://localhost:${toString config.services.nix-serve.port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin.acme@uninsane.org";
users.users.acme.uid = config.sane.allocations.acme-uid;
users.groups.acme.gid = config.sane.allocations.acme-gid;
sane.impermanence.service-dirs = [
# TODO: mode?
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
# TODO: this is overly broad; only need media and share directories to be persisted
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
];
}

View File

@@ -1,15 +1,15 @@
# docs:
# - https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
# - https://docs.pleroma.social/backend/configuration/cheatsheet/
# docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
#
# to run it in a oci-container: https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix
{ config, pkgs, ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
];
users.users.pleroma.uid = config.sane.allocations.pleroma-uid;
users.groups.pleroma.gid = config.sane.allocations.pleroma-gid;
services.pleroma.enable = true;
services.pleroma.secretConfigFile = config.sops.secrets.pleroma_secrets.path;
services.pleroma.configs = [
@@ -48,19 +48,16 @@
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
# see for reference:
# - `force_custom_plan`: <https://docs.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans>
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
database: "pleroma",
hostname: "localhost",
pool_size: 10,
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
# XXX: prepare: :named is needed only for PG <= 12
# prepare: :named,
# password: "{secrets.pleroma.db_password}",
# Configure web push notifications
@@ -113,9 +110,9 @@
systemd.services.pleroma.path = [
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
pkgs.bash
pkgs.bash
# used by Pleroma to strip geo tags from uploads
pkgs.exiftool
pkgs.exiftool
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
pkgs.gawk
# needed for email operations like password reset
@@ -125,7 +122,6 @@
systemd.services.pleroma.serviceConfig = {
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
Restart = "on-failure";
RestartSec = "10s";
};
# systemd.services.pleroma.serviceConfig = {
@@ -135,50 +131,6 @@
# CapabilityBoundingSet = lib.mkForce "~";
# };
# Pleroma server and web interface
# TODO: enable publog?
services.nginx.virtualHosts."fed.uninsane.org" = {
forceSSL = true; # pleroma redirects to https anyway
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4000";
# 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
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
if ($request_method = OPTIONS) {
return 204;
}
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
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;
# 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;
client_max_body_size 16m;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
sops.secrets.pleroma_secrets = {
sopsFile = ../../../secrets/servo.yaml;
owner = config.users.users.pleroma.name;

View File

@@ -16,7 +16,7 @@ let
};
in
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
@@ -25,61 +25,6 @@ in
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
# "/var/lib/dovecot"
];
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.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
# +a => mail passes if it originated from the A address of this domain
# +ip4:.. => mail passes if it originated from this IP
# -all => mail fails if none of these conditions were met
TXT."@" = "v=spf1 a mx -all";
# DKIM public key:
TXT."mx._domainkey" =
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkSyMufc2KrRx3j17e/LyB+3eYSBRuEFT8PUka8EDX04QzCwDPdkwgnj3GNDvnB5Ktb05Cf2SJ/S1OLqNsINxJRWtkVfZd/C339KNh9wrukMKRKNELL9HLUw0bczOI4gKKFqyrRE9qm+4csCMAR79Te9FCjGV/jVnrkLdPT0GtFwIDAQAB"
;
# DMARC fields <https://datatracker.ietf.org/doc/html/rfc7489>:
# p=none|quarantine|reject: what to do with failures
# sp = p but for subdomains
# rua = where to send aggregrate reports
# ruf = where to send individual failure reports
# fo=0|1|d|s controls WHEN to send failure reports
# (1=on bad alignment; d=on DKIM failure; s=on SPF failure);
# Additionally:
# adkim=r|s (is DKIM relaxed [default] or strict)
# aspf=r|s (is SPF relaxed [default] or strict)
# pct = sampling ratio for punishing failures (default 100 for 100%)
# rf = report format
# ri = report interval
TXT."_dmarc" =
"v=DMARC1;p=quarantine;sp=reject;rua=mailto:admin+mail@uninsane.org;ruf=mailto:admin+mail@uninsane.org;fo=1:d:s"
;
};
services.postfix.enable = true;
services.postfix.hostname = "mx.uninsane.org";
services.postfix.origin = "uninsane.org";
@@ -110,8 +55,7 @@ in
services.postfix.enableSubmissions = true;
services.postfix.submissionsOptions = submissionOptions;
systemd.services.postfix.after = [ "wireguard-wg0.service" ];
systemd.services.postfix.partOf = [ "wireguard-wg0.service" ];
systemd.services.postfix.after = [ "wg0veth.service" ];
systemd.services.postfix.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
@@ -132,8 +76,7 @@ in
# keeping this the same as the hostname seems simplest
services.opendkim.selector = "mx";
systemd.services.opendkim.after = [ "wireguard-wg0.service" ];
systemd.services.opendkim.partOf = [ "wireguard-wg0.service" ];
systemd.services.opendkim.after = [ "wg0veth.service" ];
systemd.services.opendkim.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";

View File

@@ -1,7 +1,7 @@
{ ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode?
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
];
@@ -17,11 +17,6 @@
# LC_CTYPE = "C";
# '';
# 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 = { ... }
# daily backups to /var/backup
services.postgresqlBackup.enable = true;

View File

@@ -1,7 +1,7 @@
{ pkgs, ... }:
{ ... }:
{
sane.persist.sys.plaintext = [
sane.impermanence.service-dirs = [
# TODO: mode? we need this specifically for the stats tracking in .config/
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
];
@@ -40,41 +40,11 @@
# transmission will by default not allow the world to read its files.
services.transmission.downloadDirPermissions = "775";
systemd.services.transmission.after = [ "wireguard-wg0.service" ];
systemd.services.transmission.partOf = [ "wireguard-wg0.service" ];
systemd.services.transmission.after = ["wg0veth.service"];
systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
LogLevelMax = "warning";
};
# service to automatically backup torrents i add to transmission
systemd.services.backup-torrents = {
description = "archive torrents to storage not owned by transmission";
script = ''
${pkgs.rsync}/bin/rsync -arv /var/lib/transmission/.config/transmission-daemon/torrents/ /var/backup/torrents/
'';
};
systemd.timers.backup-torrents = {
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnStartupSec = "11min";
OnUnitActiveSec = "240min";
};
};
# transmission web client
services.nginx.virtualHosts."bt.uninsane.org" = {
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9091";
proxyPass = "http://10.0.1.6:9091";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."bt" = "native";
}

View File

@@ -12,6 +12,7 @@
home = "/var/lib/gitea";
useDefaultShell = true;
group = "gitea";
uid = config.sane.allocations.git-uid;
isSystemUser = true;
# sendmail access (not 100% sure if this is necessary)
extraGroups = [ "postdrop" ];

View File

@@ -1,12 +0,0 @@
# this directory contains data of a factual nature.
# for example, public ssh keys, GPG keys, DNS-type name mappings.
#
# don't put things like fully-specific ~/.config files in here,
# even if they're "relatively unopinionated".
moduleArgs:
{
feeds = import ./feeds moduleArgs;
keys = import ./keys.nix;
}

View File

@@ -1,51 +0,0 @@
{ lib, ... }:
let
inherit (builtins) concatLists concatStringsSep foldl' fromJSON map readDir readFile;
inherit (lib) hasSuffix listToAttrs mapAttrsToList removeSuffix splitString;
# given a path to a .json file relative to sources, construct the best feed object we can.
# the .json file could be empty, in which case we make assumptions about the feed based
# on its fs path.
# Type: feedFromSourcePath :: String -> { name = String; value = feed; }
feedFromSourcePath = json-path:
assert hasSuffix "/default.json" json-path;
let
canonical-name = removeSuffix "/default.json" json-path;
default-url = "https://${canonical-name}";
feed-details = { url = default-url; } // (tryImportJson (./sources/${json-path}));
in { name = canonical-name; value = mkFeed feed-details; };
# TODO: for now, feeds are just ordinary Attrs.
# in the future, we'd like to set them up with an update script.
mkFeed = { url, ... }@details: details;
# return an AttrSet representing the json at the provided path,
# or {} if the path is empty.
tryImportJson = path:
let
as-str = readFile path;
in
if as-str == "" then
{}
else
fromJSON as-str;
sources = enumerateFilePaths ./sources;
# like `lib.listFilesRecursive` but does not mangle paths.
# Type: enumerateFilePaths :: path -> [String]
enumerateFilePaths = base:
concatLists (
mapAttrsToList
(name: type:
if type == "directory" then
# enumerate this directory and then prefix each result with the directory's name
map (e: "${name}/${e}") (enumerateFilePaths (base + "/${name}"))
else
[ name ]
)
(readDir base)
);
in
listToAttrs (map feedFromSourcePath sources)

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1369733,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Every company has a story. Learn the playbooks that built the worlds greatest companies — and how you can apply them as a founder, operator, or investor.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 173,
"last_seen": "2023-01-11T15:26:37.515527+00:00",
"last_updated": "2022-12-19T07:22:28+00:00",
"score": 18,
"self_url": "https://acquired.libsyn.com/rss",
"site_name": null,
"site_url": null,
"title": "Acquired",
"url": "https://acquired.libsyn.com/rss",
"velocity": 0.066,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1030773,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Industry veterans, degenerate gamblers & besties Chamath Palihapitiya, Jason Calacanis, David Sacks & David Friedberg cover all things economic, tech, political, social & poker.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 124,
"last_seen": "2023-01-11T12:44:53.606606+00:00",
"last_updated": "2023-01-06T10:51:00+00:00",
"score": 18,
"self_url": "https://allinchamathjason.libsyn.com/rss",
"site_name": "All-In with Chamath, Jason, Sacks & Friedberg",
"site_url": "https://allinchamathjason.libsyn.com",
"title": "All-In with Chamath, Jason, Sacks & Friedberg",
"url": "https://allinchamathjason.libsyn.com/rss",
"velocity": 0.12,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 13316,
"content_type": "application/rss+xml; charset=utf-8",
"description": "A podcast around the idea of creating a Civilizational Bootstrapper, a set of tools and technology that can be used to replicate the foundations of civilization along with itself.",
"favicon": null,
"hubs": [
"https://pubsubhubbub.appspot.com/"
],
"is_podcast": true,
"is_push": true,
"item_count": 6,
"last_seen": "2023-01-11T16:11:01.720399+00:00",
"last_updated": "2022-04-13T19:37:17+00:00",
"score": 22,
"self_url": "https://anchor.fm/s/34c7232c/podcast/rss",
"site_name": "Anchor",
"site_url": "https://anchor.fm",
"title": "Civboot",
"url": "https://anchor.fm/s/34c7232c/podcast/rss",
"velocity": 0.009,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 12669,
"content_type": "application/rss+xml; charset=utf-8",
"description": "The territory is a map of the map.",
"favicon": "http://benjaminrosshoffman.com/favicon.ico",
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 10,
"last_seen": "2023-01-11T12:32:52.176940+00:00",
"last_updated": "2023-01-09T04:33:31+00:00",
"score": -15,
"self_url": "http://benjaminrosshoffman.com/comments/feed/",
"site_name": "Compass Rose",
"site_url": "http://benjaminrosshoffman.com",
"title": "Comments for Compass Rose",
"url": "http://benjaminrosshoffman.com/comments/feed/",
"velocity": 0.312,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 56666,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Cory Doctorow's Literary Works",
"favicon": "https://craphound.com/favicon.ico",
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 20,
"last_seen": "2023-01-11T12:55:10.545856+00:00",
"last_updated": "2022-12-12T14:46:35+00:00",
"score": 12,
"self_url": "https://craphound.com/feed/",
"site_name": "Cory Doctorow's craphound.com | Cory Doctorow's Literary Works",
"site_url": "https://craphound.com",
"title": "Cory Doctorow's craphound.com",
"url": "https://craphound.com/feed/",
"velocity": 0.069,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 227480,
"content_type": "application/xml; charset=utf-8",
"description": "True stories from the dark side of the Internet",
"favicon": "https://darknetdiaries.com/imgs/favicon.png",
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 131,
"last_seen": "2023-01-11T14:49:53.136566+00:00",
"last_updated": "2022-12-27T08:00:00+00:00",
"score": 20,
"self_url": "https://darknetdiaries.com/feedfree.xml",
"site_name": "Darknet Diaries True stories from the dark side of the Internet.",
"site_url": "https://darknetdiaries.com",
"title": "Darknet Diaries (ad free)",
"url": "https://darknetdiaries.com/feedfree.xml",
"velocity": 0.067,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 66775,
"content_type": "application/rss+xml; charset=utf-8",
"description": "The Library of Economics and Liberty",
"favicon": null,
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 10,
"last_seen": "2023-01-11T10:46:38.526754+00:00",
"last_updated": "2023-01-10T05:21:31+00:00",
"score": 14,
"self_url": "https://www.econlib.org/feed/",
"site_name": "Econlib",
"site_url": "https://www.econlib.org",
"title": "Econlib",
"url": "https://www.econlib.org/feed/",
"velocity": 2.549,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 27185,
"content_type": "application/rss+xml; charset=utf-8",
"description": "The Library of Economics and Liberty",
"favicon": null,
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 10,
"last_seen": "2023-01-11T13:05:47.318206+00:00",
"last_updated": "2023-01-09T11:30:25+00:00",
"score": 14,
"self_url": "https://www.econtalk.org/feed/",
"site_name": null,
"site_url": null,
"title": "EconTalk Podcast Econlib",
"url": "https://www.econtalk.org/feed",
"velocity": 0.143,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 429348,
"content_type": "application/rss+xml; charset=utf-8",
"description": "The world's most famous whistleblower writes from exile on the intersection of technology, humanity, and power.",
"favicon": "https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/2a7d3aa2-3c2f-4196-ab7c-31541be1272e/favicon.ico",
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 16,
"last_seen": "2023-01-11T12:32:02.320483+00:00",
"last_updated": "2022-09-20T13:03:59+00:00",
"score": 14,
"self_url": "https://edwardsnowden.substack.com/feed",
"site_name": "Continuing Ed — with Edward Snowden",
"site_url": "https://edwardsnowden.substack.com",
"title": "Continuing Ed — with Edward Snowden",
"url": "https://edwardsnowden.substack.com/feed",
"velocity": 0.032,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 281377,
"content_type": "text/xml; charset=utf-8",
"description": "Matrix Live, now as an audio podcast",
"favicon": "https://feed.podbean.com/favicon.ico",
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 100,
"last_seen": "2023-01-11T15:54:24.440541+00:00",
"last_updated": "2023-01-06T16:45:00+00:00",
"score": 18,
"self_url": "https://feed.podbean.com/matrixlive/feed.xml",
"site_name": null,
"site_url": "https://feed.podbean.com",
"title": "Matrix Live",
"url": "https://feed.podbean.com/matrixlive/feed.xml",
"velocity": 0.12,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 1600578,
"content_type": "application/xml; charset=utf-8",
"description": "Design is everywhere in our lives, perhaps most importantly in the places where we've just stopped noticing. 99% Invisible is a weekly exploration of the process and power of design and architecture. From award winning producer Roman Mars. Learn more at 99percentinvisible.org.",
"favicon": null,
"hubs": [
"https://simplecast.superfeedr.com/"
],
"is_podcast": true,
"is_push": true,
"item_count": 577,
"last_seen": "2023-01-11T15:25:01.536556+00:00",
"last_updated": "2023-01-10T23:46:05+00:00",
"score": 4,
"self_url": "https://feeds.simplecast.com/BqbsxVfO",
"site_name": null,
"site_url": null,
"title": "99% Invisible",
"url": "https://feeds.simplecast.com/BqbsxVfO",
"velocity": 0.128,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 1505641,
"content_type": "text/xml; charset=utf-8",
"description": "<p>Unusually in-depth conversations about the world's most pressing problems and what you can do to solve them.<br /></p>",
"favicon": null,
"hubs": [
"https://pubsubhubbub.appspot.com/"
],
"is_podcast": true,
"is_push": true,
"item_count": 181,
"last_seen": "2023-01-11T13:29:43.501516+00:00",
"last_updated": "2023-01-09T22:57:00+00:00",
"score": 14,
"self_url": "https://feeds.backtracks.fm/feeds/80000hours/80000-hours-podcast-with-rob-wiblin/feed.xml",
"site_name": null,
"site_url": null,
"title": "80,000 Hours Podcast with Rob Wiblin",
"url": "https://feeds.feedburner.com/80000HoursPodcast",
"velocity": 0.087,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 23712,
"content_type": "text/xml; charset=utf-8",
"description": "This isn't academic history (and Carlin isn't a historian) but the podcast's unique blend of high drama, masterful narration and Twilight Zone-style twists has entertained millions of listeners.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 13,
"last_seen": "2023-01-11T15:05:34.359948+00:00",
"last_updated": "2022-03-06T19:08:44+00:00",
"score": 2,
"self_url": "https://feeds.feedburner.com/dancarlin/history?format=xml",
"site_name": null,
"site_url": null,
"title": "Dan Carlin's Hardcore History",
"url": "https://feeds.feedburner.com/dancarlin/history",
"velocity": 0.005,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 62633,
"content_type": "text/xml; charset=utf-8",
"description": "Articles, speeches, stories and novels by an award-winning science fiction writer, read aloud in small regular chunks",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 20,
"last_seen": "2023-01-11T12:57:50.103797+00:00",
"last_updated": "2022-12-12T14:46:35+00:00",
"score": 4,
"self_url": "https://craphound.com/category/podcast/feed/",
"site_name": null,
"site_url": null,
"title": "Podcast Cory Doctorow's craphound.com",
"url": "https://feeds.feedburner.com/doctorow_podcast",
"velocity": 0.068,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1315558,
"content_type": "text/xml; charset=utf-8",
"description": "Radiolab",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 150,
"last_seen": "2023-01-11T15:01:17.273650+00:00",
"last_updated": "2023-01-06T15:00:00+00:00",
"score": 4,
"self_url": "https://www.wnycstudios.org/feeds/series/podcasts",
"site_name": null,
"site_url": null,
"title": "Radiolab",
"url": "https://feeds.feedburner.com/radiolab",
"velocity": 0.139,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 2976783,
"content_type": "application/xml; charset=utf-8",
"description": "A business show about big ideas — and other problems.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 660,
"last_seen": "2023-01-11T15:51:13.652417+00:00",
"last_updated": "2023-01-10T10:00:00+00:00",
"score": 14,
"self_url": "https://feeds.megaphone.fm/recodedecode",
"site_name": null,
"site_url": null,
"title": "Decoder with Nilay Patel",
"url": "https://feeds.megaphone.fm/recodedecode",
"velocity": 0.24,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 2940192,
"content_type": "application/xml; charset=utf-8",
"description": "EconTalk: Conversations for the Curious is an award-winning weekly podcast hosted by Russ Roberts of Shalem College in Jerusalem and Stanford's Hoover Institution. The eclectic guest list includes authors, doctors, psychologists, historians, philosophers, economists, and more. Learn how the health care system really works, the serenity that comes from humility, the challenge of interpreting data, how potato chips are made, what it's like to run an upscale Manhattan restaurant, what caused the 2008 financial crisis, the nature of consciousness, and more. EconTalk has been taking the Monday out of Mondays since 2006. All 800+ episodes are available in the archive. Go to EconTalk.org for transcripts, related resources, and comments.",
"favicon": null,
"hubs": [
"https://simplecast.superfeedr.com/"
],
"is_podcast": true,
"is_push": true,
"item_count": 875,
"last_seen": "2023-01-11T14:31:49.308489+00:00",
"last_updated": "2023-01-09T11:30:00+00:00",
"score": 24,
"self_url": "https://feeds.simplecast.com/wgl4xEgL",
"site_name": null,
"site_url": null,
"title": "EconTalk",
"url": "https://feeds.simplecast.com/wgl4xEgL",
"velocity": 0.142,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 337440,
"content_type": "application/rss+xml; charset=utf-8",
"description": "A community blog devoted to refining the art of rationality",
"favicon": "https://res.cloudinary.com/lesswrong-2-0/image/upload/v1497915096/favicon_lncumn.ico",
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 10,
"last_seen": "2023-01-11T10:39:58.575828+00:00",
"last_updated": "2023-01-11T09:58:49+00:00",
"score": 32,
"self_url": "https://www.lesswrong.com/feed.xml?view=rss&karmaThreshold=2",
"site_name": "LessWrong",
"site_url": "https://www.lesswrong.com",
"title": "LessWrong",
"url": "https://www.lesswrong.com/feed.xml",
"velocity": 12.052,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 841679,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Conversations about AI, science, technology, history, philosophy and the nature of intelligence, consciousness, love, and power.",
"favicon": "https://lexfridman.com/wordpress/wp-content/uploads/2017/06/cropped-lex-favicon-4-1-32x32.png",
"hubs": [
"https://pubsubhubbub.appspot.com/"
],
"is_podcast": true,
"is_push": true,
"item_count": 300,
"last_seen": "2023-01-11T12:40:59.343327+00:00",
"last_updated": "2022-12-29T17:35:50+00:00",
"score": 20,
"self_url": "https://lexfridman.com/feed/podcast/",
"site_name": "Lex Fridman",
"site_url": "https://lexfridman.com",
"title": "Lex Fridman Podcast",
"url": "https://lexfridman.com/feed/podcast/",
"velocity": 0.265,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 83074,
"content_type": "text/xml; charset=utf-8",
"description": "projects & research",
"favicon": null,
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 14,
"last_seen": "2023-01-11T12:28:34.383284+00:00",
"last_updated": "2021-07-29T05:10:05+00:00",
"score": 14,
"self_url": "https://mg.lol/blog/rss/",
"site_name": null,
"site_url": "https://mg.lol",
"title": "MG",
"url": "https://mg.lol/blog/rss/",
"velocity": 0.004,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 3568150,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Post Reports",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 1070,
"last_seen": "2023-01-11T14:37:23.650030+00:00",
"last_updated": "2023-01-10T21:40:40+00:00",
"score": 14,
"self_url": "https://podcast.posttv.com/itunes/post-reports.xml",
"site_name": null,
"site_url": null,
"title": "Post Reports",
"url": "https://podcast.posttv.com/itunes/post-reports.xml",
"velocity": 0.711,
"version": "rss20"
}

View File

@@ -1,23 +0,0 @@
{
"bozo": 0,
"content_length": 862917,
"content_type": "application/atom+xml; charset=utf-8",
"description": "Computer history, restoring vintage computers, IC reverse engineering, and whatever",
"favicon": "https://www.blogger.com/about/favicon/favicon.ico",
"hubs": [
"http://pubsubhubbub.appspot.com/"
],
"is_podcast": false,
"is_push": true,
"item_count": 25,
"last_seen": "2023-01-11T12:29:19.820378+00:00",
"last_updated": "2023-01-10T18:21:20.265000+00:00",
"score": -2,
"self_url": "https://www.blogger.com/feeds/6264947694886887540/posts/default",
"site_name": "Blogger.com - Create a unique and beautiful blog easily.",
"site_url": "https://www.blogger.com",
"title": "Ken Shirriff's blog",
"url": "https://www.blogger.com/feeds/6264947694886887540/posts/default",
"velocity": 0.12,
"version": "atom10"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 550915,
"content_type": "application/xml; charset=utf-8",
"description": "The show that looks at the way technology is changing our economies, societies and daily lives.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 160,
"last_seen": "2023-01-11T15:31:40.303733+00:00",
"last_updated": "2022-11-22T05:00:36+00:00",
"score": 10,
"self_url": "https://feeds.acast.com/public/shows/125ef5a6-6c61-4024-b70e-3487a971a26c",
"site_name": null,
"site_url": null,
"title": "FT Tech Tonic",
"url": "https://feeds.acast.com/public/shows/125ef5a6-6c61-4024-b70e-3487a971a26c",
"velocity": 0.072,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1041745,
"content_type": "application/rss+xml; charset=utf-8",
"description": "<p>Get the best reporting and storytelling on television from 60 Minutes - on your schedule. Now you can listen to the show in its entirety every week. 60 Minutes is the most successful broadcast in television history with more than 80 Emmys under its belt. 60 Minutes offers unbiased reporting on politics, in-depth investigations and important adventures from around the world- like no one else. </p>",
"favicon": "https://rss.art19.com/favicon.ico",
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 374,
"last_seen": "2023-01-11T15:45:07.189940+00:00",
"last_updated": "2023-01-09T03:00:00+00:00",
"score": 18,
"self_url": "https://rss.art19.com/60-minutes",
"site_name": null,
"site_url": "https://rss.art19.com",
"title": "60 Minutes",
"url": "https://rss.art19.com/60-minutes",
"velocity": 0.082,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 235911,
"content_type": "application/xml; charset=utf-8",
"description": "<p>The Portal is an exploration into discovery, including conversations with thought leaders. Host Eric Weinstein, Managing Director of Thiel Capital, brings his unique expertise and diverse roster of guests for a wide range of discussions, including science, culture, business, and capitalism. The show will feature people whose lives demonstrate that portals into what we would normally consider impossible, are indeed possible.&nbsp;&nbsp;Guests include presidential candidate Andrew Yang, NY Times bestselling author Sam Harris, and retired Navy Seal and creator of the hit business podcast Jocko Willink.</p>",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 44,
"last_seen": "2023-01-11T14:47:44.995855+00:00",
"last_updated": "2020-12-02T07:50:55+00:00",
"score": -12,
"self_url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
"site_name": null,
"site_url": null,
"title": "The Portal",
"url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
"velocity": 0.082,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1462485,
"content_type": "text/xml; charset=utf-8",
"description": "Michael Malice brings his unique perspective and plenty of sick burns as he discusses everything from north Korea to American politics and culture with a bevy of guests.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 238,
"last_seen": "2023-01-11T15:58:45.264936+00:00",
"last_updated": "2023-01-04T10:40:00+00:00",
"score": -10,
"self_url": "http://origin.podcastone.com/podcast?categoryID2=2232",
"site_name": null,
"site_url": null,
"title": "\"YOUR WELCOME\" with Michael Malice",
"url": "https://www.podcastone.com/podcast?categoryID2=2232",
"velocity": 0.141,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 809084,
"content_type": "application/xml+rss; charset=utf-8",
"description": "A show that cuts through all the political drivel and media misinformation to give you a straight take on one big news story of the week.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 217,
"last_seen": "2023-01-11T13:40:50.240217+00:00",
"last_updated": "2023-01-06T10:37:50+00:00",
"score": 16,
"self_url": "https://feeds.acast.com/public/shows/1d1223a2-9d05-473b-9e79-c2b65b71d676",
"site_name": null,
"site_url": null,
"title": "Deconstructed",
"url": "https://rss.prod.firstlook.media/deconstructed/podcast.rss",
"velocity": 0.122,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 1034995,
"content_type": "application/xml+rss; charset=utf-8",
"description": "The people behind The Intercepts fearless reporting and incisive commentary discuss the crucial issues of our time.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 243,
"last_seen": "2023-01-11T14:04:41.283509+00:00",
"last_updated": "2022-12-21T10:30:43+00:00",
"score": 16,
"self_url": "https://feeds.acast.com/public/shows/f5b64019-68c3-57d4-b70b-043e63e5cbf6",
"site_name": null,
"site_url": null,
"title": "Intercepted",
"url": "https://rss.prod.firstlook.media/intercepted/podcast.rss",
"velocity": 0.112,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 3905927,
"content_type": "application/rss+xml; charset=utf-8",
"description": "The official audio version of Astral Codex Ten, with an archive of posts from Slate Star Codex. It's just me reading Scott Alexander's blog posts.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 739,
"last_seen": "2023-01-11T11:05:40.604126+00:00",
"last_updated": "2023-01-11T05:13:00+00:00",
"score": 18,
"self_url": "https://sscpodcast.libsyn.com/rss",
"site_name": "Astral Codex Ten Podcast",
"site_url": "https://sscpodcast.libsyn.com",
"title": "Astral Codex Ten Podcast",
"url": "https://sscpodcast.libsyn.com/rss",
"velocity": 0.384,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 1,
"content_length": 178687,
"content_type": "text/xml; charset=utf-8",
"description": null,
"favicon": null,
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 6,
"last_seen": "2023-01-11T10:51:13.435393+00:00",
"last_updated": "2022-10-13T00:00:00+00:00",
"score": -4,
"self_url": "https://uninsane.org/atom.xml",
"site_name": "Perfectly Sane",
"site_url": "https://uninsane.org",
"title": "Perfectly Sane",
"url": "https://uninsane.org/atom.xml",
"velocity": 0.025,
"version": "atom10"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 825822,
"content_type": "application/rss+xml; charset=utf-8",
"description": "Join neuroscientist, philosopher, and best-selling author Sam Harris as he explores questions about the human mind, society, and current events.",
"favicon": null,
"hubs": [],
"is_podcast": true,
"is_push": false,
"item_count": 326,
"last_seen": "2023-01-11T15:13:28.154435+00:00",
"last_updated": "2023-01-05T18:36:25+00:00",
"score": 18,
"self_url": "https://wakingup.libsyn.com/rss",
"site_name": "Making Sense with Sam Harris",
"site_url": "https://wakingup.libsyn.com",
"title": "Making Sense with Sam Harris",
"url": "https://wakingup.libsyn.com/rss",
"velocity": 0.096,
"version": "rss20"
}

View File

@@ -1,21 +0,0 @@
{
"bozo": 0,
"content_length": 2302,
"content_type": "text/xml; charset=utf-8",
"description": null,
"favicon": "https://xkcd.com/s/919f27.ico",
"hubs": [],
"is_podcast": false,
"is_push": false,
"item_count": 4,
"last_seen": "2023-01-11T10:29:36.530001+00:00",
"last_updated": "2023-01-09T00:00:00+00:00",
"score": 16,
"self_url": null,
"site_name": "xkcd",
"site_url": "https://xkcd.com",
"title": "xkcd.com",
"url": "https://xkcd.com/atom.xml",
"velocity": 0.429,
"version": "atom10"
}

View File

@@ -1,30 +0,0 @@
# hierarchical, DNS-like mapping from <name> => ssh host/user for that name.
# host keys are represented as user keys, just with the user specified as "root".
{
org.uninsane = rec {
root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
git.root = root;
local = {
# machine aliases i specify on my lan; not actually asserted as DNS
desko.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
desko.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
lappy.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
lappy.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
moby.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
moby.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
servo.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
servo.root = root;
};
};
com.github = rec {
# documented here: <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints>
# Github actually uses multiple keys -- one per format
root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
};
}

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