Compare commits
150 Commits
wip/feeds
...
staging/ni
Author | SHA1 | Date | |
---|---|---|---|
f5eaa635a5 | |||
47db6f0dd4 | |||
bba7efc535 | |||
358b673344 | |||
d9c101689d | |||
ccbb573681 | |||
f5c270233f | |||
bf92bb48be | |||
2037b425d6 | |||
026746a76f | |||
de74c4e7d5 | |||
34a5f3f49b | |||
ee93141c07 | |||
f2d22231a3 | |||
d5334e65ad | |||
17cdfff286 | |||
8d4ff6d4e6 | |||
5d78bc6704 | |||
4da19a6d34 | |||
2f75925678 | |||
55a1856e87 | |||
2ee0f4efe2 | |||
b0c5a5907f | |||
9af157b294 | |||
c36fed8547 | |||
a653311f04 | |||
f4d6ecb1cf | |||
c2e5a0a2fc | |||
c316e51344 | |||
f4f0c1bdd6 | |||
6a2374e046 | |||
708cb841fe | |||
094b7223c7 | |||
f6dfc9cf29 | |||
7c2ab92302 | |||
7c18d77046 | |||
02f316f7f8 | |||
df848b3262 | |||
1e67b5c97e | |||
a3a7b6c563 | |||
e527beb9d0 | |||
2e942e2dd4 | |||
f46e3fdf01 | |||
038a9034d7 | |||
5a232eb832 | |||
9301b95dbb | |||
d13bcc49ab | |||
35e28041cd | |||
58a5a8b56d | |||
e6d4ff3c6a | |||
be29ad8bd8 | |||
0fb8e2c867 | |||
580c1b74cb | |||
f8595f1ed6 | |||
1deda148bb | |||
5bbef18130 | |||
6967c331e2 | |||
9202345beb | |||
17a8cabc09 | |||
bc190f90bd | |||
bb983a5328 | |||
0e8fc29b01 | |||
4e14f063fc | |||
10d69fb0a4 | |||
7aac965e32 | |||
98ae1a8513 | |||
72a2ab78f3 | |||
18c98feb34 | |||
487af9b492 | |||
472d25c056 | |||
9eafacad12 | |||
0eb46a3179 | |||
ddb184b5ff | |||
194a6b6cf4 | |||
016384aa2b | |||
b4e19c037e | |||
bd504f6c83 | |||
bdd309eb15 | |||
eedc1170ec | |||
5a586c6e3c | |||
371bcad650 | |||
926decbea5 | |||
c0f76ea8d8 | |||
40fc37930f | |||
30e7eb9ab6 | |||
2e03f47edc | |||
4d552e3f0f | |||
176a98879d | |||
fc70889c34 | |||
49b4c57826 | |||
5111d095ac | |||
fe15cdd705 | |||
638420ea0d | |||
d55dd5ace6 | |||
45695aed6b | |||
d6e79c4d07 | |||
380ceaf625 | |||
942c581107 | |||
b6d94c2e08 | |||
fd7acc8fc8 | |||
db670fc172 | |||
6438971c8c | |||
e439d398b6 | |||
0f25cba331 | |||
39959e912d | |||
62e649743d | |||
b1741a18e1 | |||
a829a8e027 | |||
d742ae83bd | |||
110ab1a794 | |||
7d5a81e542 | |||
1af2a3f329 | |||
3fa9e910a9 | |||
6befc40700 | |||
29db2d8dc5 | |||
36d8052982 | |||
48115231a3 | |||
8b56ddd1ca | |||
c1457f5bfb | |||
7dfaf77a71 | |||
72dc7029e6 | |||
95f3215b00 | |||
baac8df8c2 | |||
dc6a08a12b | |||
2413e2eb5f | |||
7327128493 | |||
ed8059f4c4 | |||
3a72295610 | |||
e6d9edf27d | |||
78782d5f7e | |||
91275f3723 | |||
8115edea8d | |||
4c475bbf9c | |||
7040e1f07c | |||
aafa64942c | |||
a44a99e371 | |||
a7ff90c843 | |||
d4996d6f31 | |||
bd5209c655 | |||
9588108fd5 | |||
942e302afb | |||
2bd98e6764 | |||
7b9910f287 | |||
917afe209e | |||
cc5cf9b6f4 | |||
57d95dd298 | |||
0b78df53be | |||
c8dcb4ac59 | |||
241f4ae58f | |||
965d7eedbb |
72
flake.lock
generated
72
flake.lock
generated
@@ -39,11 +39,11 @@
|
||||
"mobile-nixos": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1670131242,
|
||||
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
|
||||
"lastModified": 1674593731,
|
||||
"narHash": "sha256-RHJOhxqPKLxbLLabbj1IEuUQqO76TOTtmpxYyTtfDmU=",
|
||||
"owner": "nixos",
|
||||
"repo": "mobile-nixos",
|
||||
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
|
||||
"rev": "4d2093efa7efa00131d385fd9d11e54ce16bc57a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -53,42 +53,45 @@
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unpatched"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1672953546,
|
||||
"narHash": "sha256-oz757DnJ1ITvwyTovuwG3l9cX6j9j6/DH9eH+cXFJmc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a518c77148585023ff56022f09c4b2c418a51ef5",
|
||||
"type": "github"
|
||||
"lastModified": 1,
|
||||
"narHash": "sha256-P5BXhRIaKF6ze3am7CY//NFOJ4ihoys9h0ka9S15VV4=",
|
||||
"path": "/nix/store/41f11k8hk1qjd440mavrybc7xgrcp9gj-source/nixpatches",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
"path": "/nix/store/41f11k8hk1qjd440mavrybc7xgrcp9gj-source/nixpatches",
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1673163619,
|
||||
"narHash": "sha256-B33PFBL64ZgTWgMnhFL3jgheAN/DjHPsZ1Ih3z0VE5I=",
|
||||
"owner": "NixOS",
|
||||
"lastModified": 1674407282,
|
||||
"narHash": "sha256-2qwc8mrPINSFdWffPK+ji6nQ9aGnnZyHSItVcYDZDlk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8c54d842d9544361aac5f5b212ba04e4089e8efe",
|
||||
"rev": "ab1254087f4cdf4af74b552d7fc95175d9bdbb49",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-22.11",
|
||||
"type": "indirect"
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1673100377,
|
||||
"narHash": "sha256-mT76pTd0YFxT6CwtPhDgHJhuIgLY+ZLSMiQpBufwMG4=",
|
||||
"lastModified": 1674352297,
|
||||
"narHash": "sha256-OkAnJPrauEcUCrst4/3DKoQfUn2gXKuU6CFvhtMrLgg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9f11a2df77cb945c115ae2a65f53f38121597d73",
|
||||
"rev": "918b760070bb8f48cb511300fcd7e02e13058a2e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -98,14 +101,31 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1674459583,
|
||||
"narHash": "sha256-L0UZl/u2H3HGsrhN+by42c5kNYeKtdmJiPzIRvEVeiM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1b1f50645af2a70dc93eae18bfd88d330bfbcf7f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"home-manager": "home-manager",
|
||||
"mobile-nixos": "mobile-nixos",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||
"sops-nix": "sops-nix",
|
||||
"uninsane": "uninsane"
|
||||
"uninsane-dot-org": "uninsane-dot-org"
|
||||
}
|
||||
},
|
||||
"sops-nix": {
|
||||
@@ -116,11 +136,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1673147300,
|
||||
"narHash": "sha256-gR9OEfTzWfL6vG0qkbn1TlBAOlg4LuW8xK/u0V41Ihc=",
|
||||
"lastModified": 1674546403,
|
||||
"narHash": "sha256-vkyNv0xzXuEnu9v52TUtRugNmQWIti8c2RhYnbLG71w=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "2253120d2a6147e57bafb5c689e086221df8032f",
|
||||
"rev": "b6ab3c61e2ca5e07d1f4eb1b67304e2670ea230c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -129,7 +149,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"uninsane": {
|
||||
"uninsane-dot-org": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
|
228
flake.nix
228
flake.nix
@@ -1,24 +1,48 @@
|
||||
# docs:
|
||||
# - <https://nixos.wiki/wiki/Flakes>
|
||||
# FLAKE FEEDBACK:
|
||||
# - if flake inputs are meant to be human-readable, a human should be able to easily track them down given the URL.
|
||||
# - this is not the case with registry URLs, like `nixpkgs/nixos-22.11`.
|
||||
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
|
||||
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
|
||||
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
|
||||
# - need some way to apply local patches to inputs.
|
||||
#
|
||||
#
|
||||
# DEVELOPMENT DOCS:
|
||||
# - Flake docs: <https://nixos.wiki/wiki/Flakes>
|
||||
# - Flake RFC: <https://github.com/tweag/rfcs/blob/flakes/rfcs/0049-flakes.md>
|
||||
# - Discussion: <https://github.com/NixOS/rfcs/pull/49>
|
||||
# - <https://serokell.io/blog/practical-nix-flakes>
|
||||
|
||||
{
|
||||
# XXX: use the `github:` scheme instead of the more readable git+https: because it's *way* more efficient
|
||||
# preferably, i would rewrite the human-readable https URLs to nix-specific github: URLs with a helper,
|
||||
# but `inputs` is required to be a strict attrset: not an expression.
|
||||
inputs = {
|
||||
nixpkgs-stable.url = "nixpkgs/nixos-22.11";
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
# <https://github.com/nixos/nixpkgs/tree/nixos-22.11>
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
|
||||
|
||||
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
||||
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
nixpkgs = {
|
||||
url = "./nixpatches";
|
||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||
};
|
||||
mobile-nixos = {
|
||||
# <https://github.com/nixos/mobile-nixos>
|
||||
url = "github:nixos/mobile-nixos";
|
||||
flake = false;
|
||||
};
|
||||
home-manager = {
|
||||
url = "github:nix-community/home-manager/release-22.05";
|
||||
# <https://github.com/nix-community/home-manager/tree/release-22.05>
|
||||
url = "github:nix-community/home-manager?ref=release-22.05";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
sops-nix = {
|
||||
# <https://github.com/Mic92/sops-nix>
|
||||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
uninsane = {
|
||||
uninsane-dot-org = {
|
||||
url = "git+https://git.uninsane.org/colin/uninsane";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
@@ -28,59 +52,56 @@
|
||||
self,
|
||||
nixpkgs,
|
||||
nixpkgs-stable,
|
||||
nixpkgs-unpatched,
|
||||
mobile-nixos,
|
||||
home-manager,
|
||||
sops-nix,
|
||||
uninsane
|
||||
}: let
|
||||
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
|
||||
name = "nixpkgs-patched-uninsane";
|
||||
src = nixpkgs;
|
||||
patches = import ./nixpatches/list.nix {
|
||||
inherit (nixpkgs.legacyPackages.${system}) fetchpatch;
|
||||
inherit (nixpkgs.lib) fakeHash;
|
||||
};
|
||||
};
|
||||
# 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-host = { name, local, target }:
|
||||
uninsane-dot-org
|
||||
}:
|
||||
let
|
||||
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;
|
||||
modules = [
|
||||
./modules
|
||||
(import ./hosts/instantiate.nix name)
|
||||
home-manager.nixosModule
|
||||
sops-nix.nixosModules.sops
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(import "${mobile-nixos}/overlay/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; };
|
||||
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
||||
|
||||
# cross-compatible packages
|
||||
# gocryptfs = cross.gocryptfs;
|
||||
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
|
||||
self.overlays.pins
|
||||
];
|
||||
}
|
||||
];
|
||||
});
|
||||
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"; };
|
||||
};
|
||||
|
||||
# pinned packages:
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
decl-bootable-host = { name, local, target }: rec {
|
||||
nixosConfiguration = decl-host { inherit name local target; };
|
||||
# unofficial output
|
||||
# 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)
|
||||
@@ -94,40 +115,77 @@
|
||||
# - 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`
|
||||
img = nixosConfiguration.config.system.build.img;
|
||||
};
|
||||
hosts.servo = decl-bootable-host { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.desko = decl-bootable-host { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.lappy = decl-bootable-host { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
hosts.moby = decl-bootable-host { 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.
|
||||
hosts.moby-cross = decl-bootable-host { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
|
||||
hosts.rescue = decl-bootable-host { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
|
||||
in {
|
||||
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) hosts;
|
||||
imgs = builtins.mapAttrs (name: value: value.img) hosts;
|
||||
packages = let
|
||||
allPkgsFor = sys: (customPackagesFor sys sys) // {
|
||||
nixpkgs = nixpkgsFor sys sys;
|
||||
uninsane = uninsane.packages."${sys}";
|
||||
imgs = builtins.mapAttrs (_: host-dfn: host-dfn.config.system.build.img) self.nixosConfigurations;
|
||||
|
||||
overlays = rec {
|
||||
default = pkgs;
|
||||
pkgs = import ./overlays/pkgs.nix;
|
||||
pins = import ./overlays/pins.nix; # TODO: move to `nixpatches/` input
|
||||
passthru =
|
||||
let
|
||||
stable = next: prev: {
|
||||
stable = nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
|
||||
};
|
||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||
uninsane = uninsane-dot-org.overlay;
|
||||
in
|
||||
next: prev:
|
||||
(stable next prev) // (mobile next prev) // (uninsane next prev);
|
||||
};
|
||||
in {
|
||||
x86_64-linux = allPkgsFor "x86_64-linux";
|
||||
aarch64-linux = allPkgsFor "aarch64-linux";
|
||||
};
|
||||
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";
|
||||
|
||||
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 = {
|
||||
# use like `nix run '.#init-feed' uninsane.org`
|
||||
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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -6,12 +6,16 @@
|
||||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
||||
sane.services.duplicity.enable = true;
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/desko.yaml;
|
||||
sane.persist.enable = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
@@ -19,7 +23,7 @@
|
||||
services.usbmuxd.enable = true;
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
@@ -41,7 +45,7 @@
|
||||
};
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
};
|
||||
|
||||
programs.steam = {
|
@@ -1,9 +1,13 @@
|
||||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
||||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
# sane.users.guest.enable = true;
|
||||
@@ -14,7 +18,7 @@
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/lappy.yaml;
|
||||
sopsFile = ../../../secrets/lappy.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
@@ -6,6 +6,10 @@
|
||||
./kernel.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
||||
|
||||
# cross-compiled documentation is *slow*.
|
||||
# no obvious way to natively compile docs (2022/09/29).
|
||||
# entrypoint is nixos/modules/misc/documentation.nix
|
||||
@@ -19,7 +23,7 @@
|
||||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/moby.yaml;
|
||||
sopsFile = ../../../secrets/moby.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
@@ -54,9 +58,10 @@
|
||||
|
||||
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
||||
# this is because they can't allocate enough video ram.
|
||||
# the default CMA seems to be 32M. we could probably get by with as little as 64M, and safely with 128M.
|
||||
# the default CMA seems to be 32M.
|
||||
# i was running fine with 256MB from 2022/07-ish through 2022/12-ish, but then the phone quit reliably coming back from sleep: maybe a memory leak?
|
||||
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||
boot.kernelParams = [ "cma=256M" ];
|
||||
boot.kernelParams = [ "cma=384M" ];
|
||||
|
||||
# mobile-nixos' /lib/firmware includes:
|
||||
# rtl_bt (bluetooth)
|
@@ -125,6 +125,9 @@ 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;
|
||||
})
|
||||
))
|
||||
];
|
@@ -1,29 +1,32 @@
|
||||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./net.nix
|
||||
./users.nix
|
||||
./secrets.nix
|
||||
./services
|
||||
];
|
||||
|
||||
sane.packages.extraUserPkgs = [
|
||||
sane.packages.extraUserPkgs = with pkgs; [
|
||||
# for administering services
|
||||
pkgs.matrix-synapse
|
||||
pkgs.freshrss
|
||||
freshrss
|
||||
matrix-synapse
|
||||
signaldctl
|
||||
];
|
||||
sane.persist.enable = true;
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
|
||||
# automatically log in at the virtual consoles.
|
||||
# using root here makes sure we always have an escape hatch
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
# both transmission and ipfs try to set different net defaults.
|
||||
# we just use the most aggressive of the two here:
|
||||
boot.kernel.sysctl = {
|
@@ -52,18 +52,18 @@
|
||||
|
||||
# services.resolved.extraConfig = ''
|
||||
# # docs: `man resolved.conf`
|
||||
# # DNS servers to use via the `wg0` interface.
|
||||
# # DNS servers to use via the `wg-ovpns` interface.
|
||||
# # i hope that from the root ns, these aren't visible.
|
||||
# DNS=46.227.67.134%wg0 192.165.9.158%wg0
|
||||
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
|
||||
# FallbackDNS=1.1.1.1 9.9.9.9
|
||||
# '';
|
||||
|
||||
# OVPN CONFIG (https://www.ovpn.com):
|
||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||
# if you `systemctl restart wireguard-wg0`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||
networking.wireguard.enable = true;
|
||||
networking.wireguard.interfaces.wg0 = let
|
||||
networking.wireguard.interfaces.wg-ovpns = let
|
||||
ip = "${pkgs.iproute2}/bin/ip";
|
||||
in-ns = "${ip} netns exec ovpns";
|
||||
iptables = "${pkgs.iptables}/bin/iptables";
|
||||
@@ -159,13 +159,10 @@
|
||||
# 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
|
||||
5 ovpns
|
||||
'';
|
||||
networking.iproute2.enable = true;
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
41
hosts/by-name/servo/secrets.nix
Normal file
41
hosts/by-name/servo/secrets.nix
Normal file
@@ -0,0 +1,41 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."duplicity_passphrase" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
sopsFile = ../../../secrets/servo/mautrix_signal_env.bin;
|
||||
};
|
||||
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
@@ -24,8 +24,4 @@ lib.mkIf false
|
||||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
@@ -27,8 +27,4 @@ lib.mkIf false
|
||||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
@@ -46,6 +46,8 @@
|
||||
}];
|
||||
|
||||
# provide access to certs
|
||||
# TODO: this should just be `acme`. then we also add nginx to the `acme` group.
|
||||
# why is /var/lib/acme/* owned by `nginx` group??
|
||||
users.users.ejabberd.extraGroups = [ "nginx" ];
|
||||
|
||||
security.acme.certs."uninsane.org".extraDomainNames = [
|
@@ -11,8 +11,7 @@
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{
|
||||
sops.secrets.freshrss_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
owner = config.users.users.freshrss.name;
|
||||
mode = "0400";
|
||||
};
|
||||
@@ -42,7 +41,10 @@
|
||||
description = "import sane RSS feed list";
|
||||
after = [ "freshrss-config.service" ];
|
||||
script = ''
|
||||
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
|
||||
# easiest way to preserve feeds: delete the user, recreate it, import feeds
|
||||
${pkgs.freshrss}/cli/delete-user.php --user colin || true
|
||||
${pkgs.freshrss}/cli/create-user.php --user colin --password "$(cat ${config.services.freshrss.passwordFile})" || true
|
||||
${pkgs.freshrss}/cli/import-for-user.php --user colin --filename ${opml}
|
||||
'';
|
||||
};
|
||||
|
@@ -15,6 +15,17 @@
|
||||
services.gitea.settings.session.COOKIE_SECURE = true;
|
||||
# services.gitea.disableRegistration = true;
|
||||
|
||||
# gitea doesn't create the git user
|
||||
users.users.git = {
|
||||
description = "Gitea Service";
|
||||
home = "/var/lib/gitea";
|
||||
useDefaultShell = true;
|
||||
group = "gitea";
|
||||
isSystemUser = true;
|
||||
# sendmail access (not 100% sure if this is necessary)
|
||||
extraGroups = [ "postdrop" ];
|
||||
};
|
||||
|
||||
services.gitea.settings = {
|
||||
server = {
|
||||
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
|
@@ -7,8 +7,8 @@
|
||||
];
|
||||
services.jackett.enable = true;
|
||||
|
||||
systemd.services.jackett.after = [ "wireguard-wg0.service" ];
|
||||
systemd.services.jackett.partOf = [ "wireguard-wg0.service" ];
|
||||
systemd.services.jackett.after = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.jackett.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
@@ -6,8 +6,12 @@
|
||||
imports = [
|
||||
./discord-puppet.nix
|
||||
# ./irc.nix
|
||||
./signal.nix
|
||||
];
|
||||
|
||||
# allow synapse to read the registration files of its appservices
|
||||
users.users.matrix-synapse.extraGroups = [ "mautrix-signal" ];
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
||||
];
|
||||
@@ -127,8 +131,7 @@
|
||||
};
|
||||
|
||||
|
||||
sops.secrets.matrix_synapse_secrets = {
|
||||
sopsFile = ../../../../secrets/servo.yaml;
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
@@ -43,6 +43,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
# TODO: should use a dedicated user
|
||||
systemd.services.mx-puppet-discord.serviceConfig = {
|
||||
# fix up to not use /var/lib/private, but just /var/lib
|
||||
DynamicUser = lib.mkForce false;
|
34
hosts/by-name/servo/services/matrix/signal.nix
Normal file
34
hosts/by-name/servo/services/matrix/signal.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
# config options:
|
||||
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
services.signald.enable = true;
|
||||
services.mautrix-signal.enable = true;
|
||||
services.mautrix-signal.environmentFile =
|
||||
config.sops.secrets.mautrix_signal_env.path;
|
||||
|
||||
services.mautrix-signal.settings.signal.socket_path = "/run/signald/signald.sock";
|
||||
services.mautrix-signal.settings.homeserver.domain = "uninsane.org";
|
||||
services.mautrix-signal.settings.bridge.permissions."@colin:uninsane.org" = "admin";
|
||||
services.matrix-synapse.settings.app_service_config_files = [
|
||||
# auto-created by mautrix-signal service
|
||||
"/var/lib/mautrix-signal/signal-registration.yaml"
|
||||
];
|
||||
|
||||
systemd.services.mautrix-signal.serviceConfig = {
|
||||
# allow communication to signald
|
||||
SupplementaryGroups = [ "signald" ];
|
||||
ReadWritePaths = [ "/run/signald" ];
|
||||
};
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "mautrix-signal"; group = "mautrix-signal"; directory = "/var/lib/mautrix-signal"; }
|
||||
];
|
||||
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
format = "binary";
|
||||
mode = "0440";
|
||||
owner = config.users.users.mautrix-signal.name;
|
||||
group = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
@@ -1,11 +1,8 @@
|
||||
{ ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
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"; }
|
||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/navidrome"; }
|
||||
];
|
||||
services.navidrome.enable = true;
|
||||
services.navidrome.settings = {
|
||||
@@ -18,6 +15,20 @@
|
||||
ScanSchedule = "@every 1h";
|
||||
};
|
||||
|
||||
systemd.services.navidrome.serviceConfig = {
|
||||
# fix to use a normal user so we can configure perms correctly
|
||||
DynamicUser = lib.mkForce false;
|
||||
User = "navidrome";
|
||||
Group = "navidrome";
|
||||
};
|
||||
|
||||
users.groups.navidrome = {};
|
||||
|
||||
users.users.navidrome = {
|
||||
group = "navidrome";
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."music.uninsane.org" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
@@ -17,5 +17,5 @@
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/servo.yaml;
|
||||
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
|
||||
}
|
@@ -111,7 +111,7 @@
|
||||
''
|
||||
];
|
||||
|
||||
systemd.services.pleroma.path = [
|
||||
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
|
||||
# used by Pleroma to strip geo tags from uploads
|
||||
@@ -135,6 +135,11 @@
|
||||
# CapabilityBoundingSet = lib.mkForce "~";
|
||||
# };
|
||||
|
||||
# this is required to allow pleroma to send email.
|
||||
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
|
||||
# hack to fix that.
|
||||
users.users.pleroma.extraGroups = [ "postdrop" ];
|
||||
|
||||
# Pleroma server and web interface
|
||||
# TODO: enable publog?
|
||||
services.nginx.virtualHosts."fed.uninsane.org" = {
|
||||
@@ -179,8 +184,7 @@
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||
|
||||
sops.secrets.pleroma_secrets = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
owner = config.users.users.pleroma.name;
|
||||
};
|
||||
}
|
@@ -110,8 +110,8 @@ 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 = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.postfix.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.postfix.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
@@ -132,8 +132,8 @@ 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 = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.opendkim.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.opendkim.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
||||
@@ -197,8 +197,7 @@ in
|
||||
# }
|
||||
];
|
||||
|
||||
sops.secrets.dovecot_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
owner = config.users.users.dovecot2.name;
|
||||
# TODO: debug why mail can't be sent without this being world-readable
|
||||
mode = "0444";
|
@@ -40,8 +40,8 @@
|
||||
# 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 = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||
systemd.services.transmission.serviceConfig = {
|
||||
# run this behind the OVPN static VPN
|
||||
NetworkNamespacePath = "/run/netns/ovpns";
|
@@ -8,7 +8,6 @@ lib.mkIf false
|
||||
{
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
owner = config.users.users.mediawiki.name;
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
services.mediawiki.enable = true;
|
@@ -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" ];
|
||||
};
|
||||
}
|
22
hosts/common/cross.nix
Normal file
22
hosts/common/cross.nix
Normal file
@@ -0,0 +1,22 @@
|
||||
{ config, ... }:
|
||||
|
||||
let
|
||||
mkCrossFrom = localSystem: pkgs: import pkgs.path {
|
||||
inherit localSystem;
|
||||
crossSystem = pkgs.stdenv.hostPlatform.system;
|
||||
inherit (config.nixpkgs) config overlays;
|
||||
};
|
||||
in
|
||||
{
|
||||
# the configuration of which specific package set `pkgs.cross` refers to happens elsewhere;
|
||||
# here we just define them all.
|
||||
nixpkgs.overlays = [
|
||||
(next: prev: {
|
||||
# non-emulated packages build *from* local *for* target.
|
||||
# for large packages like the linux kernel which are expensive to build under emulation,
|
||||
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
|
||||
crossFrom."x86_64-linux" = mkCrossFrom "x86_64-linux" next;
|
||||
crossFrom."aarch64-linux" = mkCrossFrom "aarch64-linux" next;
|
||||
})
|
||||
];
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./bluetooth.nix
|
||||
./cross.nix
|
||||
./feeds.nix
|
||||
./fs.nix
|
||||
./hardware
|
||||
./hardware.nix
|
||||
./i2p.nix
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
@@ -29,6 +29,9 @@
|
||||
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
|
||||
];
|
||||
|
||||
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
|
||||
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# time.timeZone = "America/Los_Angeles";
|
||||
@@ -38,6 +41,11 @@
|
||||
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 = {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ ... }:
|
||||
{ lib, sane-data, ... }:
|
||||
let
|
||||
hourly = { freq = "hourly"; };
|
||||
daily = { freq = "daily"; };
|
||||
@@ -12,6 +12,8 @@ let
|
||||
tech = { cat = "tech"; };
|
||||
uncat = { cat = "uncat"; };
|
||||
|
||||
text = { format = "text"; };
|
||||
|
||||
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
||||
# format-specific helpers
|
||||
mkText = mkRss "text";
|
||||
@@ -21,48 +23,77 @@ let
|
||||
# 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 = [
|
||||
(mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
|
||||
(fromDb "lexfridman.com/podcast" // rat)
|
||||
## Astral Codex Ten
|
||||
(mkPod "http://feeds.libsyn.com/108018/rss" // rat // daily)
|
||||
(fromDb "sscpodcast.libsyn.com" // rat)
|
||||
## Econ Talk
|
||||
(mkPod "https://feeds.simplecast.com/wgl4xEgL" // rat // daily)
|
||||
## Cory Doctorow
|
||||
(mkPod "https://feeds.feedburner.com/doctorow_podcast" // pol // infrequent)
|
||||
(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
|
||||
(mkPod "https://anchor.fm/s/34c7232c/podcast/rss" // tech // infrequent)
|
||||
(mkPod "https://feeds.feedburner.com/80000HoursPodcast" // rat // weekly)
|
||||
(mkPod "https://allinchamathjason.libsyn.com/rss" // pol // weekly)
|
||||
(mkPod "https://acquired.libsyn.com/rss" // tech // infrequent)
|
||||
(mkPod "https://rss.acast.com/deconstructed" // 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)
|
||||
(mkPod "https://rss.acast.com/intercepted-with-jeremy-scahill" // pol // weekly)
|
||||
(mkPod "https://podcast.posttv.com/itunes/post-reports.xml" // pol // weekly)
|
||||
# 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
|
||||
(mkPod "https://rss.art19.com/the-portal" // rat // infrequent)
|
||||
(mkPod "https://feeds.megaphone.fm/darknetdiaries" // tech // infrequent)
|
||||
(mkPod "http://feeds.wnyc.org/radiolab" // pol // infrequent)
|
||||
(mkPod "https://wakingup.libsyn.com/rss" // pol // infrequent)
|
||||
## 99% Invisible
|
||||
(mkPod "https://feeds.simplecast.com/BqbsxVfO" // pol // infrequent)
|
||||
(mkPod "https://rss.acast.com/ft-tech-tonic" // tech // infrequent)
|
||||
(mkPod "https://feeds.feedburner.com/dancarlin/history?format=xml" // rat // infrequent)
|
||||
## 60 minutes (NB: this features more than *just* audio?)
|
||||
(mkPod "https://www.cbsnews.com/latest/rss/60-minutes" // pol // infrequent)
|
||||
(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
|
||||
(mkPod "https://feeds.megaphone.fm/recodedecode" // tech // weekly)
|
||||
(fromDb "feeds.megaphone.fm/recodedecode" // tech)
|
||||
## Matrix (chat) Live
|
||||
(mkPod "https://feed.podbean.com/matrixlive/feed.xml" // tech // weekly)
|
||||
## Michael Malice - Your Welcome
|
||||
(mkPod "https://www.podcastone.com/podcast?categoryID2=2232" // pol // weekly)
|
||||
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
|
||||
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||
(fromDb "rss.art19.com/your-welcome" // pol)
|
||||
(fromDb "seattlenice.buzzsprout.com" // pol)
|
||||
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
|
||||
];
|
||||
|
||||
texts = [
|
||||
# AGGREGATORS (> 1 post/day)
|
||||
(mkText "https://www.lesswrong.com/feed.xml" // rat // hourly)
|
||||
(mkText "http://www.econlib.org/index.xml" // pol // hourly)
|
||||
(fromDb "lwn.net" // tech)
|
||||
(fromDb "lesswrong.com" // rat)
|
||||
(fromDb "econlib.org" // pol)
|
||||
|
||||
# AGGREGATORS (< 1 post/day)
|
||||
(mkText "https://palladiummag.com/feed" // uncat // weekly)
|
||||
@@ -75,10 +106,13 @@ let
|
||||
(mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly)
|
||||
|
||||
# DEVELOPERS
|
||||
(mkText "https://uninsane.org/atom.xml" // infrequent // tech)
|
||||
(mkText "https://mg.lol/blog/rss/" // infrequent // tech)
|
||||
(fromDb "uninsane.org" // tech)
|
||||
(fromDb "mg.lol" // tech)
|
||||
(fromDb "drewdevault.com" // tech)
|
||||
## Ken Shirriff
|
||||
(mkText "https://www.righto.com/feeds/posts/default" // tech // infrequent)
|
||||
(fromDb "righto.com" // tech)
|
||||
## shared blog by a few NixOS devs, notably onny
|
||||
(fromDb "project-insanity.org" // tech)
|
||||
## Vitalik Buterin
|
||||
(mkText "https://vitalik.ca/feed.xml" // tech // infrequent)
|
||||
## ian (Sanctuary)
|
||||
@@ -94,7 +128,7 @@ let
|
||||
(mkText "https://pomeroyb.com/feed.xml" // tech // infrequent)
|
||||
|
||||
# (TECH; POL) COMMENTATORS
|
||||
(mkSubstack "edwardsnowden" // pol // infrequent)
|
||||
(fromDb "edwardsnowden.substack.com" // pol // text)
|
||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||
## Ben Thompson
|
||||
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
||||
@@ -148,4 +182,11 @@ let
|
||||
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;
|
||||
}
|
||||
|
@@ -21,6 +21,12 @@
|
||||
sane.ids.freshrss.uid = 2401;
|
||||
sane.ids.freshrss.gid = 2401;
|
||||
sane.ids.mediawiki.uid = 2402;
|
||||
sane.ids.signald.uid = 2403;
|
||||
sane.ids.signald.gid = 2403;
|
||||
sane.ids.mautrix-signal.uid = 2404;
|
||||
sane.ids.mautrix-signal.gid = 2404;
|
||||
sane.ids.navidrome.uid = 2405;
|
||||
sane.ids.navidrome.gid = 2405;
|
||||
|
||||
sane.ids.colin.uid = 1000;
|
||||
sane.ids.guest.uid = 1100;
|
||||
|
@@ -1,16 +1,6 @@
|
||||
{ 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>
|
||||
@@ -30,14 +20,4 @@
|
||||
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" ];
|
||||
};
|
||||
}
|
||||
|
@@ -1,24 +1,33 @@
|
||||
{ config, lib, sane-data, sane-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) head map mapAttrs tail;
|
||||
inherit (lib) concatStringsSep mkMerge reverseList;
|
||||
in
|
||||
{
|
||||
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);
|
||||
rev = reverseList path;
|
||||
name = head rev;
|
||||
host = concatStringsSep "." (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
|
||||
domainKeys = sane-lib.flattenAttrs (
|
||||
mapAttrs (host: cfg: {
|
||||
colin = cfg.ssh.user_pubkey;
|
||||
root = cfg.ssh.host_pubkey;
|
||||
}) config.sane.hosts.by-name
|
||||
);
|
||||
in mkMerge (map
|
||||
({ path, value }: {
|
||||
"${keyNameForPath path}" = value;
|
||||
"${keyNameForPath path}" = lib.mkIf (value != null) value;
|
||||
})
|
||||
(globalKeys ++ localKeys)
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
}
|
||||
|
@@ -86,6 +86,7 @@ in
|
||||
"Pictures"
|
||||
"Videos"
|
||||
|
||||
".cache/nix"
|
||||
".cargo"
|
||||
".rustup"
|
||||
];
|
||||
@@ -125,8 +126,8 @@ in
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
permitRootLogin = "no";
|
||||
passwordAuthentication = false;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -1,10 +1,27 @@
|
||||
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||
|
||||
hostName: { ... }: {
|
||||
# args from flake-level `import`
|
||||
{ hostName, localSystem }:
|
||||
|
||||
# module args
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./${hostName}
|
||||
./by-name/${hostName}
|
||||
./common
|
||||
./modules
|
||||
];
|
||||
|
||||
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}";
|
||||
})
|
||||
];
|
||||
}
|
||||
|
12
hosts/modules/default.nix
Normal file
12
hosts/modules/default.nix
Normal file
@@ -0,0 +1,12 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./derived-secrets.nix
|
||||
./hardware
|
||||
./hostnames.nix
|
||||
./hosts.nix
|
||||
./roles
|
||||
./wg-home.nix
|
||||
];
|
||||
}
|
47
hosts/modules/derived-secrets.nix
Normal file
47
hosts/modules/derived-secrets.nix
Normal file
@@ -0,0 +1,47 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) toString;
|
||||
inherit (lib) mapAttrs mkOption types;
|
||||
cfg = config.sane.derived-secrets;
|
||||
secret = types.submodule {
|
||||
options = {
|
||||
len = mkOption {
|
||||
type = types.int;
|
||||
};
|
||||
encoding = mkOption {
|
||||
type = types.enum [ "base64" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.derived-secrets = mkOption {
|
||||
type = types.attrsOf secret;
|
||||
default = {};
|
||||
description = ''
|
||||
fs path => secret options.
|
||||
for each entry, we create an item at the given path whose value is deterministic,
|
||||
but also pseudo-random and not predictable by anyone without root access to the machine.
|
||||
as PRNG source we use the host ssh key, and derived secrets are salted based on the destination path.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.fs = mapAttrs (path: c: {
|
||||
generated.script.script = ''
|
||||
echo "$1" | cat /dev/stdin /etc/ssh/host_keys/ssh_host_ed25519_key \
|
||||
| sha512sum \
|
||||
| cut -c 1-${toString (c.len * 2)} \
|
||||
| tr a-z A-Z \
|
||||
| basenc -d --base16 \
|
||||
| basenc --${c.encoding} \
|
||||
> "$1"
|
||||
'';
|
||||
generated.script.scriptArgs = [ path ];
|
||||
generated.acl.mode = "0600";
|
||||
}) cfg;
|
||||
};
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./all.nix
|
||||
./x86_64.nix
|
||||
];
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
{ lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
config = mkIf (pkgs.system == "x86_64-linux") {
|
||||
config = lib.mkIf (pkgs.system == "x86_64-linux") {
|
||||
boot.initrd.availableKernelModules = [
|
||||
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
11
hosts/modules/hostnames.nix
Normal file
11
hosts/modules/hostnames.nix
Normal file
@@ -0,0 +1,11 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# 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 = lib.mapAttrs' (host: cfg: {
|
||||
name = cfg.lan-ip;
|
||||
value = [ host ];
|
||||
}) config.sane.hosts.by-name;
|
||||
}
|
100
hosts/modules/hosts.nix
Normal file
100
hosts/modules/hosts.nix
Normal file
@@ -0,0 +1,100 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) attrValues filterAttrs mkMerge mkOption types;
|
||||
cfg = config.sane.hosts;
|
||||
|
||||
host = types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
ssh.user_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey that the primary user of this machine will use when connecting to other machines.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
ssh.host_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey which this host will present to connections initiated against it.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
wg-home.pubkey = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
wireguard public key for the wg-home VPN.
|
||||
e.g. "pWtnKW7f7sNIZQ2M83uJ7cHg3IL1tebE3IoVkCgjkXM=".
|
||||
'';
|
||||
};
|
||||
wg-home.ip = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
IP address to use on the wg-home VPN.
|
||||
e.g. "10.0.10.5";
|
||||
'';
|
||||
};
|
||||
wg-home.endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
lan-ip = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ip address when on the lan.
|
||||
e.g. "192.168.0.5";
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.hosts.by-name = mkOption {
|
||||
type = types.attrsOf host;
|
||||
default = {};
|
||||
description = ''
|
||||
map of hostname => attrset of information specific to that host,
|
||||
like its ssh pubkey, etc.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# TODO: this should be populated per-host
|
||||
sane.hosts.by-name."desko" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
|
||||
wg-home.ip = "10.0.10.22";
|
||||
lan-ip = "192.168.0.22";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."lappy" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
|
||||
wg-home.ip = "10.0.10.20";
|
||||
lan-ip = "192.168.0.20";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."moby" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||
wg-home.pubkey = "I7XIR1hm8bIzAtcAvbhWOwIAabGkuEvbWH/3kyIB1yA=";
|
||||
wg-home.ip = "10.0.10.48";
|
||||
lan-ip = "192.168.0.48";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."servo" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
|
||||
wg-home.ip = "10.0.10.5";
|
||||
wg-home.endpoint = "uninsane.org:51820";
|
||||
lan-ip = "192.168.0.5";
|
||||
};
|
||||
};
|
||||
}
|
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
@@ -0,0 +1,18 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
# 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" ];
|
||||
};
|
||||
};
|
||||
}
|
17
hosts/modules/roles/client/default.nix
Normal file
17
hosts/modules/roles/client/default.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./bluetooth-pairings.nix
|
||||
./wifi-pairings.nix
|
||||
];
|
||||
|
||||
# option is consumed by the other imports in this dir
|
||||
options.sane.roles.client = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
}
|
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
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" ];
|
||||
};
|
||||
};
|
||||
}
|
6
hosts/modules/roles/default.nix
Normal file
6
hosts/modules/roles/default.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./client
|
||||
];
|
||||
}
|
80
hosts/modules/wg-home.nix
Normal file
80
hosts/modules/wg-home.nix
Normal file
@@ -0,0 +1,80 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) filter map;
|
||||
inherit (lib) concatMap mapAttrsToList mkIf mkMerge mkOption optionalAttrs types;
|
||||
cfg = config.sane.services.wg-home;
|
||||
server-cfg = config.sane.hosts.by-name."servo".wg-home;
|
||||
mkPeer = { ips, pubkey, endpoint }: {
|
||||
publicKey = pubkey;
|
||||
allowedIPs = map (k: "${k}/32") ips;
|
||||
} // (optionalAttrs (endpoint != null) {
|
||||
inherit endpoint;
|
||||
# send keepalives every 25 seconds to keep NAT routes live.
|
||||
# only need to do this from client -> server though, i think.
|
||||
persistentKeepalive = 25;
|
||||
# allows wireguard to notice DNS/hostname changes, with this much effective TTL.
|
||||
dynamicEndpointRefreshSeconds = 600;
|
||||
});
|
||||
# make separate peers to route each given host
|
||||
mkClientPeers = hosts: map (p: mkPeer {
|
||||
inherit (p) pubkey endpoint;
|
||||
ips = [ p.ip ];
|
||||
}) hosts;
|
||||
# make a single peer which routes all the given hosts
|
||||
mkServerPeer = hosts: mkPeer {
|
||||
inherit (server-cfg) pubkey endpoint;
|
||||
ips = map (h: h.ip) hosts;
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.services.wg-home.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# generate a (deterministic) wireguard private key
|
||||
sane.derived-secrets."/run/wg-home.priv" = {
|
||||
len = 32;
|
||||
encoding = "base64";
|
||||
};
|
||||
|
||||
# wireguard VPN which allows everything on my domain to speak to each other even when
|
||||
# not behind a shared LAN.
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
preSetup =
|
||||
let
|
||||
gen-key = config.sane.fs."/run/wg-home.priv".unit;
|
||||
in
|
||||
"${pkgs.systemd}/bin/systemctl start '${gen-key}'";
|
||||
|
||||
ips = [
|
||||
"${cfg.ip}/24"
|
||||
];
|
||||
|
||||
peers =
|
||||
let
|
||||
all-peers = mapAttrsToList (_: hostcfg: hostcfg.wg-home) config.sane.hosts.by-name;
|
||||
peer-list = filter (p: p.ip != null && p.ip != cfg.ip && p.pubkey != null) all-peers;
|
||||
in
|
||||
if cfg.ip == server-cfg.ip then
|
||||
# if we're the server, then we maintain the entire client list
|
||||
mkClientPeers peer-list
|
||||
else
|
||||
# but if we're a client, we maintain a single peer -- the server -- which does the actual routing
|
||||
[ (mkServerPeer peer-list) ];
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
{ config, ... }:
|
||||
|
||||
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
||||
{
|
||||
# automatically log in at the virtual consoles.
|
||||
# using root here makes sure we always have an escape hatch
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
# gitea doesn't create the git user
|
||||
users.users.git = {
|
||||
description = "Gitea Service";
|
||||
home = "/var/lib/gitea";
|
||||
useDefaultShell = true;
|
||||
group = "gitea";
|
||||
isSystemUser = true;
|
||||
# sendmail access (not 100% sure if this is necessary)
|
||||
extraGroups = [ "postdrop" ];
|
||||
};
|
||||
|
||||
# this is required to allow pleroma to send email.
|
||||
# raw `sendmail` works, but i think pleroma's passing it some funny flags or something, idk.
|
||||
# hack to fix that.
|
||||
users.users.pleroma.extraGroups = [ "postdrop" ];
|
||||
}
|
@@ -2,21 +2,19 @@
|
||||
|
||||
let
|
||||
inherit (builtins) concatLists concatStringsSep foldl' fromJSON map readDir readFile;
|
||||
inherit (lib) init mapAttrsToList removePrefix removeSuffix splitString;
|
||||
inherit (lib.attrsets) recursiveUpdate setAttrByPath;
|
||||
inherit (lib.filesystem) listFilesRecursive;
|
||||
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 -> { path = [String]; value = feed; }
|
||||
# Type: feedFromSourcePath :: String -> { name = String; value = feed; }
|
||||
feedFromSourcePath = json-path:
|
||||
assert hasSuffix "/default.json" json-path;
|
||||
let
|
||||
canonical-name = removeSuffix "/default" (lib.removeSuffix ".json" json-path);
|
||||
canonical-name = removeSuffix "/default.json" json-path;
|
||||
default-url = "https://${canonical-name}";
|
||||
attr-path = splitString "/" canonical-name;
|
||||
feed-details = { url = default-url; } // (tryImportJson (./sources/${json-path}));
|
||||
in { path = attr-path; value = mkFeed feed-details; };
|
||||
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.
|
||||
@@ -49,10 +47,5 @@ let
|
||||
)
|
||||
(readDir base)
|
||||
);
|
||||
|
||||
# like listToAttrs, except takes { path, value } pairs instead of { name, value } pairs.
|
||||
# Type: listToAttrsByPath :: [{ path = [String]; value = Any; }] -> Attrs
|
||||
listToAttrsByPath = items:
|
||||
foldl' (acc: { path, value }: recursiveUpdate acc (setAttrByPath path value)) {} items;
|
||||
in
|
||||
listToAttrsByPath (map feedFromSourcePath sources)
|
||||
listToAttrs (map feedFromSourcePath sources)
|
||||
|
21
modules/data/feeds/sources/acquired.libsyn.com/default.json
Normal file
21
modules/data/feeds/sources/acquired.libsyn.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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 world’s 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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/craphound.com/default.json
Normal file
21
modules/data/feeds/sources/craphound.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/darknetdiaries.com/default.json
Normal file
21
modules/data/feeds/sources/darknetdiaries.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/drewdevault.com/default.json
Normal file
21
modules/data/feeds/sources/drewdevault.com/default.json
Normal file
File diff suppressed because one or more lines are too long
21
modules/data/feeds/sources/econlib.org/default.json
Normal file
21
modules/data/feeds/sources/econlib.org/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/econtalk.org/default.json
Normal file
21
modules/data/feeds/sources/econtalk.org/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/lesswrong.com/default.json
Normal file
21
modules/data/feeds/sources/lesswrong.com/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/lwn.net/default.json
Normal file
21
modules/data/feeds/sources/lwn.net/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 14068,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "LWN.net is a comprehensive source of news and opinions from\n and about the Linux community. This is the main LWN.net feed,\n listing all articles which are posted to the site front page.",
|
||||
"favicon": "https://static.lwn.net/images/favicon.png",
|
||||
"favicon_data_uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB9oMHRALH2oogOQAAAdtSURBVFjDvZd7cFT1Fcc/v9+9d3N3N9nNxg3ZhCS8CYgpQnjYqaZQpaI1QMTxAbb2ga0dZ7T4oNOhY9FxHKtjp1Y7lo6tODqOtk5hSgdHBJwq1ALDIITwSgIhPEISNmRDdrP3+esfCxkYCE1G2t/MnfuY8zvne875nvM7F4a/JgKvAkeA7vP3PwAV/B/WNCANqAuXGQyqhx9brqqqq21g5nAVymHKvwSELv5gmiYBzWBEcYkBvAfk/a+814D+i70f5Fo8XKVDXWHgmSUPPsT462/g8P5G4vE4r61+m0hRjERlJS2HDgL0AX8fqlJ9qII/euGDqXF1mrr588hmLTZ9vJHNX+yl9WgLP37kcTZt2czmf6xH6sY4x+rnmgMYMarq7m9PqGbdhk/At3ngZ89jux6/fullZn7jZuoXP8j2A6fp6Tge2/7RO9ceQJ4RPLrm/b+pWyYrMSJ0homjjnFwdzMvrxhNe7KX/qzF1rWrSaeSwyKWGI7wfbUlmTUvRoIBQyC0SqAPIYt478NWbpkpqFzYCGDdf/8Px9bX181y3Uzr0qVLv7xmAF54aMK564oK8pc90I/v+rz5vuBcWjHteo1wvs57B2+nYmQZNdNr7IL8cMDzXP9sT8+WbqG+993589u/MoBJU6p7nr8nHD2b7sWxFWNHa3x9us2h4zNwJz5JqifJdfE4BxobaWs7Rn393QCcPHXqj7fPu+0nX7UR3blo8b3RW5dvJDFmJpPGhdHDU8i7aR+h6SuRQqFrGmv+/Cdc16WhoYFAIIBpmhRGo9OGTMINGzbkZbPecj1PnyZ8lcpY1sePPfrU5x0dLW+sfPoJ8vND3LzkNSzLpqgoyr7GRnRdZ+tn/6SpqYmOzg5mzZQkEgm6u7uJx+MoIdSQAKxa9anueNmPRlYk5oZDIRSQzdoPv/3Oaid5NmVc0FNYWIDjuOzZ0wD4rFu3llmzZrNj5w7C4TAliQR1dQtI9fTQfuokpeUVQyvDaTXZlWVlJXMjBQVIKfE8j7xIHoXRiFHuODTsO4AZDFJZMZJDTU3s27+fb9XW0tvbi5XNUlNTw8JF9TQ2NGBZ2RzJhGDbtn9FgFKg/aocKIrHRkQjEVzXxbIspJQIIVBKoUtJLBbF0DXajjTjWf3EzTZaPnuO9vac3u/cVYenFKZpEo8XM2HCRLZs+ZS33nqrqnRkxaP/NQLKsVfZtv19XddDAEqpAS8uvGuaxJIaBXkdzB77CVogQNms15FCIoRC+Yqx48blvBOCxsZGlK9ELB4vbT95/OpVUFtb2+U47l8uNujYNplMBt/3UUrh+z5SN4j1bkHhczDvdXRNglAIIdCkAKWQQuD7Pr999XdMqhrftX/P7meH1AfGTZrUKly3ctmyZaJuwUK6u7vxPI+CggjBoJkzomlomQ4ssxgJeJ6Hrml4/gWyK5RSCCmRCE53nN4755u1U4fUB0ZXjm3r7OoSe/fuxfc8zvWl6T3Xx8lTp+hLZwbkXNdH+j7K95FS4rhuzqjI+SV1PZc6IQgEAtkhT0SbN25YMLK8Yv+TT61ANwzKSkuIRiNomkY2m8X3fVzXJUOQsMzgK1AKpJRImQuoPJ+GHBSFrhvucE7Dnmd/9czXKs8stZOqXsqy+ygqLKSkuBiVIwYK8NBAOblc4yOFBAVSk/i+j57ezxj35yAVnzfXTRvWYZT9YqIfMEcJVIbDJ6L4E15BiJzigY1CI+h0kdaL8F2Xzs5Okskz3DD1RiSCUWfuwsyvAsB3jrJrl55qbmGO8DMnlrxy+MygEdi+uropECgUiBCIEOMTh2jrP0S/WYVhGKAUnu8jpcDL+qTOneXEieN4nneefwrfasIMhgcmPqmXM316S3RGjbFbKUFponr13KcbHrkkAsfWVjvxuKXrhsIwJwLGeYUWdn8zLbH1aEKi8AGBJiU4faS6kzSf6CISjVIcL+ZUZ5I5+Y8TKRwNBAeq4qL4cqDxtDVl6R7zkgiUlwtd6FXkaHxRZkSAgDmS0vZ6tBnbyWQypNNp+vv70aRJNBajJjGKolgM0wyxYdPvufNWHwgMkmmX0oQVuCwFrp/FEPIKtBAgo0QLujj2758yZt67ADiOQzLZTZ5ZSqwwCkBfX4Zx1hvoRslVBu4QBfmeGKQM1aBcFXqCsvydZFKnATAMg0SiZMB4Nmtxxx3zqb9NBxkaRI8LXs9Aa78EQO7Bu+j/4rKxFE1T7HrnDizLwbZtspZFT6qXD9euJxg02bVjG7oOKOcKhjuw+w/Tk+qkqSnkXJYCXymwm5BCIGQQRCG5SjiPUaWwHRAKTDNwRf8WzB6BbQmMQC9Ci+Qc8rvpS5/laGuIk23B1o6m1OQfrDmUvQzA5i35I8DbHApRZQYxCsIpES/uJBTKNRvbFhw/kc+BI9le4JfAGSAJ58sCxjS29dUebS1dWl6exgwexnOhvT1EU7PZmtnz5fh7/4o3rKH03Sem35Qf9d4MBf3JAiVSfcbWe57bXXu1PR/84sYVRYXei3lBIfrOkbGTeZMW/Wbn8cHk/wNEUBaFSM2AYgAAAABJRU5ErkJggg==",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 15,
|
||||
"last_updated": "2023-01-22T15:53:01+00:00",
|
||||
"score": 18,
|
||||
"self_url": "",
|
||||
"site_name": "Welcome to LWN.net [LWN.net]",
|
||||
"site_url": "https://lwn.net",
|
||||
"title": "LWN.net",
|
||||
"url": "https://lwn.net/headlines/newrss",
|
||||
"velocity": 2.78,
|
||||
"version": "rss10"
|
||||
}
|
21
modules/data/feeds/sources/mg.lol/default.json
Normal file
21
modules/data/feeds/sources/mg.lol/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
21
modules/data/feeds/sources/project-insanity.org/default.json
Normal file
21
modules/data/feeds/sources/project-insanity.org/default.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 11426,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Awesome hacking blog",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": false,
|
||||
"is_push": false,
|
||||
"item_count": 10,
|
||||
"last_updated": "2023-01-01T22:57:33+00:00",
|
||||
"score": 12,
|
||||
"self_url": "https://blog.project-insanity.org/feed/",
|
||||
"site_name": "project-insanity.org",
|
||||
"site_url": "https://blog.project-insanity.org",
|
||||
"title": "project-insanity.org",
|
||||
"url": "https://blog.project-insanity.org/feed/",
|
||||
"velocity": 0.026,
|
||||
"version": "rss20"
|
||||
}
|
23
modules/data/feeds/sources/righto.com/default.json
Normal file
23
modules/data/feeds/sources/righto.com/default.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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. 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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1034995,
|
||||
"content_type": "application/xml+rss; charset=utf-8",
|
||||
"description": "The people behind The Intercept’s 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"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user