Compare commits
2 Commits
archive/do
...
wip/flake-
Author | SHA1 | Date | |
---|---|---|---|
05649d1a22 | |||
cc5d706c1b |
82
flake.lock
generated
82
flake.lock
generated
@@ -15,14 +15,35 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"home-manager": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667907331,
|
||||||
|
"narHash": "sha256-bHkAwkYlBjkupPUFcQjimNS8gxWSWjOTevEuwdnp5m0=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"rev": "6639e3a837fc5deb6f99554072789724997bc8e5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"ref": "release-22.05",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1674880620,
|
"lastModified": 1670131242,
|
||||||
"narHash": "sha256-JMALuC7xcoH/T66sKTVLuItHfOJBCWsNKpE49Qrvs80=",
|
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"rev": "7478a9ffad737486951186b66f6c5535dc5802e2",
|
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -35,26 +56,43 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-unpatched"
|
"nixpkgs-unpatched"
|
||||||
]
|
],
|
||||||
|
"patches": []
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1,
|
"lastModified": 1,
|
||||||
"narHash": "sha256-arp7Uy7ct5ryTcmSY032eN7hr33i7D2XvjTRLliCFDc=",
|
"narHash": "sha256-d3XSehPFkNwvwlOYy7gch0NLxOgdXuV7j5r/Qsn7kHc=",
|
||||||
"path": "/nix/store/jblp2g67p3wid2qarcyd8bzrbs9wg5lb-source/nixpatches",
|
"path": "nixpatches",
|
||||||
"type": "path"
|
"type": "path"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"path": "/nix/store/jblp2g67p3wid2qarcyd8bzrbs9wg5lb-source/nixpatches",
|
"path": "nixpatches",
|
||||||
"type": "path"
|
"type": "path"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1674352297,
|
"lastModified": 1673704454,
|
||||||
"narHash": "sha256-OkAnJPrauEcUCrst4/3DKoQfUn2gXKuU6CFvhtMrLgg=",
|
"narHash": "sha256-5Wdj1MgdOgn3+dMFIBtg+IAYZApjF8JzwLWDPieg0C4=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a83ed85c14fcf242653df6f4b0974b7e1c73c6c6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-22.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1673740915,
|
||||||
|
"narHash": "sha256-MMH8zONfqahgHly3K8/A++X34800rajA/XgZ2DzNL/M=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "918b760070bb8f48cb511300fcd7e02e13058a2e",
|
"rev": "7c65528c3f8462b902e09d1ccca23bb9034665c2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -66,11 +104,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unpatched": {
|
"nixpkgs-unpatched": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1674641431,
|
"lastModified": 1673631141,
|
||||||
"narHash": "sha256-qfo19qVZBP4qn5M5gXc/h1MDgAtPA5VxJm9s8RUAkVk=",
|
"narHash": "sha256-AprpYQ5JvLS4wQG/ghm2UriZ9QZXvAwh1HlgA/6ZEVQ=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9b97ad7b4330aacda9b2343396eb3df8a853b4fc",
|
"rev": "befc83905c965adfd33e5cae49acb0351f6e0404",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -82,8 +120,10 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"home-manager": "home-manager",
|
||||||
"mobile-nixos": "mobile-nixos",
|
"mobile-nixos": "mobile-nixos",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"uninsane-dot-org": "uninsane-dot-org"
|
"uninsane-dot-org": "uninsane-dot-org"
|
||||||
@@ -94,14 +134,14 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1674546403,
|
"lastModified": 1673752321,
|
||||||
"narHash": "sha256-vkyNv0xzXuEnu9v52TUtRugNmQWIti8c2RhYnbLG71w=",
|
"narHash": "sha256-EFfXY1ZHJq4FNaNQA9x0djtu/jiOhBbT0Xi+BT06cJw=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "b6ab3c61e2ca5e07d1f4eb1b67304e2670ea230c",
|
"rev": "e18eefd2b133a58309475298052c341c08470717",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -118,11 +158,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1675131883,
|
"lastModified": 1666870107,
|
||||||
"narHash": "sha256-yBgJDG72YqIr1bltasqHD1E/kHc9uRFgDjxDmy6kI8M=",
|
"narHash": "sha256-b9eXZxSwhzdJI5uQgfrMhu4SY2POrPkinUg7F5gQVYo=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "b099c24091cc192abf3997b94342d4b31cc5757b",
|
"rev": "80c6ec95bd430e29d231cf745f19279bb76fb382",
|
||||||
"revCount": 170,
|
"revCount": 164,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.uninsane.org/colin/uninsane"
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
},
|
},
|
||||||
|
33
flake.nix
33
flake.nix
@@ -19,19 +19,27 @@
|
|||||||
# but `inputs` is required to be a strict attrset: not an expression.
|
# but `inputs` is required to be a strict attrset: not an expression.
|
||||||
inputs = {
|
inputs = {
|
||||||
# <https://github.com/nixos/nixpkgs/tree/nixos-22.11>
|
# <https://github.com/nixos/nixpkgs/tree/nixos-22.11>
|
||||||
# nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
|
nixpkgs-stable.url = "github:nixos/nixpkgs?ref=nixos-22.11";
|
||||||
|
|
||||||
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
# <https://github.com/nixos/nixpkgs/tree/nixos-unstable>
|
||||||
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs-unpatched.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
nixpkgs = {
|
nixpkgs = {
|
||||||
url = "./nixpatches";
|
url = "path:nixpatches";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
|
# XXX: `path:` urls have poor UX in that they still get "locked" and require manual updates as if they were remote.
|
||||||
|
# by linking back to ourselves here, we can update `nixpatches/list.nix` *without* having to run `nix flake update` afterward.
|
||||||
|
inputs.patches.follows = "";
|
||||||
};
|
};
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
# <https://github.com/nixos/mobile-nixos>
|
# <https://github.com/nixos/mobile-nixos>
|
||||||
url = "github:nixos/mobile-nixos";
|
url = "github:nixos/mobile-nixos";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
home-manager = {
|
||||||
|
# <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 = {
|
sops-nix = {
|
||||||
# <https://github.com/Mic92/sops-nix>
|
# <https://github.com/Mic92/sops-nix>
|
||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
@@ -46,12 +54,13 @@
|
|||||||
outputs = {
|
outputs = {
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
|
nixpkgs-stable,
|
||||||
nixpkgs-unpatched,
|
nixpkgs-unpatched,
|
||||||
mobile-nixos,
|
mobile-nixos,
|
||||||
|
home-manager,
|
||||||
sops-nix,
|
sops-nix,
|
||||||
uninsane-dot-org,
|
uninsane-dot-org
|
||||||
...
|
}:
|
||||||
}@inputs:
|
|
||||||
let
|
let
|
||||||
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
nixpkgsCompiledBy = local: nixpkgs.legacyPackages."${local}";
|
||||||
|
|
||||||
@@ -117,12 +126,9 @@
|
|||||||
pins = import ./overlays/pins.nix; # TODO: move to `nixpatches/` input
|
pins = import ./overlays/pins.nix; # TODO: move to `nixpatches/` input
|
||||||
passthru =
|
passthru =
|
||||||
let
|
let
|
||||||
stable =
|
stable = next: prev: {
|
||||||
if inputs ? "nixpkgs-stable" then (
|
stable = nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
|
||||||
next: prev: {
|
};
|
||||||
stable = inputs.nixpkgs-stable.legacyPackages."${prev.stdenv.hostPlatform.system}";
|
|
||||||
}
|
|
||||||
) else (next: prev: {});
|
|
||||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||||
uninsane = uninsane-dot-org.overlay;
|
uninsane = uninsane-dot-org.overlay;
|
||||||
in
|
in
|
||||||
@@ -135,6 +141,7 @@
|
|||||||
sane = import ./modules;
|
sane = import ./modules;
|
||||||
passthru = { ... }: {
|
passthru = { ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
|
home-manager.nixosModule
|
||||||
sops-nix.nixosModules.sops
|
sops-nix.nixosModules.sops
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -166,7 +173,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
init-feed = {
|
init-feed = {
|
||||||
# use like `nix run '.#init-feed' uninsane.org`
|
|
||||||
type = "app";
|
type = "app";
|
||||||
program = "${pkgs.feeds.passthru.initFeedScript}";
|
program = "${pkgs.feeds.passthru.initFeedScript}";
|
||||||
};
|
};
|
||||||
@@ -182,6 +188,9 @@
|
|||||||
description = "python environment for data processing";
|
description = "python environment for data processing";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# unofficial output; used by inputs.nixpatches
|
||||||
|
nixpatches = import ./nixpatches/list.nix;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
# config options:
|
|
||||||
# - <https://github.com/mautrix/signal/blob/master/mautrix_signal/example-config.yaml>
|
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
|
||||||
sane.persist.sys.plaintext = [
|
|
||||||
{ user = "mautrix-signal"; group = "mautrix-signal"; directory = "/var/lib/mautrix-signal"; }
|
|
||||||
{ user = "signald"; group = "signald"; directory = "/var/lib/signald"; }
|
|
||||||
];
|
|
||||||
|
|
||||||
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" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
sops.secrets."mautrix_signal_env" = {
|
|
||||||
format = "binary";
|
|
||||||
mode = "0440";
|
|
||||||
owner = config.users.users.mautrix-signal.name;
|
|
||||||
group = config.users.users.matrix-synapse.name;
|
|
||||||
};
|
|
||||||
}
|
|
16
hosts/common/bluetooth.nix
Normal file
16
hosts/common/bluetooth.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{ 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" ];
|
||||||
|
};
|
||||||
|
}
|
@@ -1,28 +1,34 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./bluetooth.nix
|
||||||
./cross.nix
|
./cross.nix
|
||||||
./feeds.nix
|
./feeds.nix
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./hardware.nix
|
./hardware
|
||||||
./home
|
|
||||||
./i2p.nix
|
./i2p.nix
|
||||||
./ids.nix
|
./ids.nix
|
||||||
./machine-id.nix
|
./machine-id.nix
|
||||||
./net.nix
|
./net.nix
|
||||||
./persist.nix
|
|
||||||
./secrets.nix
|
./secrets.nix
|
||||||
./ssh.nix
|
./ssh.nix
|
||||||
./users.nix
|
./users.nix
|
||||||
./vpn.nix
|
./vpn.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
sane.home-manager.enable = true;
|
||||||
sane.nixcache.enable-trusted-keys = true;
|
sane.nixcache.enable-trusted-keys = true;
|
||||||
sane.packages.enableConsolePkgs = true;
|
sane.packages.enableConsolePkgs = true;
|
||||||
sane.packages.enableSystemPkgs = true;
|
sane.packages.enableSystemPkgs = true;
|
||||||
|
|
||||||
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
|
sane.persist.sys.plaintext = [
|
||||||
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
"/var/log"
|
||||||
|
"/var/backup" # for e.g. postgres dumps
|
||||||
|
# TODO: move elsewhere
|
||||||
|
"/var/lib/alsa" # preserve output levels, default devices
|
||||||
|
"/var/lib/colord" # preserve color calibrations (?)
|
||||||
|
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
|
||||||
|
];
|
||||||
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
@@ -39,6 +45,7 @@
|
|||||||
"nixpkgs-overlays=${../..}/overlays"
|
"nixpkgs-overlays=${../..}/overlays"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# TODO: move this into home-manager?
|
||||||
fonts = {
|
fonts = {
|
||||||
enableDefaultFonts = true;
|
enableDefaultFonts = true;
|
||||||
fonts = with pkgs; [ font-awesome twitter-color-emoji hack-font ];
|
fonts = with pkgs; [ font-awesome twitter-color-emoji hack-font ];
|
||||||
@@ -66,20 +73,8 @@
|
|||||||
# NIXOS_OZONE_WL = "1";
|
# NIXOS_OZONE_WL = "1";
|
||||||
# LIBGL_ALWAYS_SOFTWARE = "1";
|
# LIBGL_ALWAYS_SOFTWARE = "1";
|
||||||
};
|
};
|
||||||
|
# enable zsh completions
|
||||||
# dconf docs: <https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/desktop_migration_and_administration_guide/profiles>
|
environment.pathsToLink = [ "/share/zsh" ];
|
||||||
# find keys/values with `dconf dump /`
|
|
||||||
programs.dconf.enable = true;
|
|
||||||
programs.dconf.packages = [
|
|
||||||
(pkgs.writeTextFile {
|
|
||||||
name = "dconf-user-profile";
|
|
||||||
destination = "/etc/dconf/profile/user";
|
|
||||||
text = ''
|
|
||||||
user-db:user
|
|
||||||
system-db:site
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
# link debug symbols into /run/current-system/sw/lib/debug
|
# link debug symbols into /run/current-system/sw/lib/debug
|
||||||
# hopefully picked up by gdb automatically?
|
# hopefully picked up by gdb automatically?
|
||||||
|
@@ -13,7 +13,6 @@ let
|
|||||||
uncat = { cat = "uncat"; };
|
uncat = { cat = "uncat"; };
|
||||||
|
|
||||||
text = { format = "text"; };
|
text = { format = "text"; };
|
||||||
img = { format = "image"; };
|
|
||||||
|
|
||||||
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
mkRss = format: url: { inherit url format; } // uncat // infrequent;
|
||||||
# format-specific helpers
|
# format-specific helpers
|
||||||
@@ -30,16 +29,16 @@ let
|
|||||||
in {
|
in {
|
||||||
url = raw.url;
|
url = raw.url;
|
||||||
# not sure the exact mapping with velocity here: entries per day?
|
# not sure the exact mapping with velocity here: entries per day?
|
||||||
freq = lib.mkIf (raw.velocity or 0 != 0) (lib.mkDefault (
|
freq = lib.mkDefault (
|
||||||
if raw.velocity > 2 then
|
if raw.velocity or 0 > 2 then
|
||||||
"hourly"
|
"hourly"
|
||||||
else if raw.velocity > 0.5 then
|
else if raw.velocity or 0 > 0.5 then
|
||||||
"daily"
|
"daily"
|
||||||
else if raw.velocity > 0.1 then
|
else if raw.velocity or 0 > 0.1 then
|
||||||
"weekly"
|
"weekly"
|
||||||
else
|
else
|
||||||
"infrequent"
|
"infrequent"
|
||||||
));
|
);
|
||||||
} // lib.optionalAttrs (raw.is_podcast or false) {
|
} // lib.optionalAttrs (raw.is_podcast or false) {
|
||||||
format = "podcast";
|
format = "podcast";
|
||||||
} // lib.optionalAttrs (raw.title or "" != "") {
|
} // lib.optionalAttrs (raw.title or "" != "") {
|
||||||
@@ -48,17 +47,16 @@ let
|
|||||||
|
|
||||||
podcasts = [
|
podcasts = [
|
||||||
(fromDb "lexfridman.com/podcast" // rat)
|
(fromDb "lexfridman.com/podcast" // rat)
|
||||||
|
# (mkPod "https://lexfridman.com/feed/podcast/" // rat // weekly)
|
||||||
## Astral Codex Ten
|
## Astral Codex Ten
|
||||||
(fromDb "sscpodcast.libsyn.com" // rat)
|
(fromDb "sscpodcast.libsyn.com" // rat)
|
||||||
## Econ Talk
|
## Econ Talk
|
||||||
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
|
(fromDb "feeds.simplecast.com/wgl4xEgL" // rat)
|
||||||
## Cory Doctorow -- both podcast & text entries
|
## Cory Doctorow -- both podcast & text entries
|
||||||
(fromDb "craphound.com" // pol)
|
(fromDb "craphound.com" // pol)
|
||||||
(fromDb "congressionaldish.libsyn.com" // pol)
|
(mkPod "https://congressionaldish.libsyn.com/rss" // pol // infrequent)
|
||||||
## Civboot -- https://anchor.fm/civboot
|
## Civboot -- https://anchor.fm/civboot
|
||||||
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
||||||
## Emerge: making sense of what's next -- <https://www.whatisemerging.com/emergepodcast>
|
|
||||||
(mkPod "https://anchor.fm/s/21bc734/podcast/rss" // pol // infrequent)
|
|
||||||
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
|
(fromDb "feeds.feedburner.com/80000HoursPodcast" // rat)
|
||||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||||
(fromDb "acquired.libsyn.com" // tech)
|
(fromDb "acquired.libsyn.com" // tech)
|
||||||
@@ -87,108 +85,93 @@ let
|
|||||||
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
|
(fromDb "feed.podbean.com/matrixlive/feed.xml" // tech)
|
||||||
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||||
(fromDb "rss.art19.com/your-welcome" // pol)
|
(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 = [
|
texts = [
|
||||||
# AGGREGATORS (> 1 post/day)
|
# AGGREGATORS (> 1 post/day)
|
||||||
(fromDb "lwn.net" // tech)
|
|
||||||
(fromDb "lesswrong.com" // rat)
|
(fromDb "lesswrong.com" // rat)
|
||||||
(fromDb "econlib.org" // pol)
|
(fromDb "econlib.org" // pol)
|
||||||
|
|
||||||
# AGGREGATORS (< 1 post/day)
|
# AGGREGATORS (< 1 post/day)
|
||||||
(fromDb "palladiummag.com" // uncat)
|
(mkText "https://palladiummag.com/feed" // uncat // weekly)
|
||||||
(fromDb "profectusmag.com" // uncat)
|
(mkText "https://profectusmag.com/feed" // uncat // weekly)
|
||||||
(fromDb "semiaccurate.com" // tech)
|
(mkText "https://semiaccurate.com/feed" // tech // weekly)
|
||||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||||
(fromDb "spectrum.ieee.org" // tech)
|
(mkText "https://spectrum.ieee.org/rss" // tech // weekly)
|
||||||
## n.b.: quality RSS list here: <https://forum.merveilles.town/thread/57/share-your-rss-feeds%21-6/>
|
|
||||||
(mkText "https://forum.merveilles.town/rss.xml" // pol // infrequent)
|
|
||||||
|
|
||||||
## No Moods, Ads or Cutesy Fucking Icons
|
## No Moods, Ads or Cutesy Fucking Icons
|
||||||
(fromDb "rifters.com/crawl" // uncat)
|
(mkText "https://www.rifters.com/crawl/?feed=rss2" // uncat // weekly)
|
||||||
|
|
||||||
# DEVELOPERS
|
# DEVELOPERS
|
||||||
(fromDb "uninsane.org" // tech)
|
(fromDb "uninsane.org" // tech)
|
||||||
(fromDb "mg.lol" // tech)
|
(fromDb "mg.lol" // tech)
|
||||||
(fromDb "drewdevault.com" // tech)
|
|
||||||
## Ken Shirriff
|
## Ken Shirriff
|
||||||
(fromDb "righto.com" // tech)
|
(fromDb "righto.com" // tech)
|
||||||
## shared blog by a few NixOS devs, notably onny
|
|
||||||
(fromDb "project-insanity.org" // tech)
|
|
||||||
## Vitalik Buterin
|
## Vitalik Buterin
|
||||||
(fromDb "vitalik.ca" // tech)
|
(mkText "https://vitalik.ca/feed.xml" // tech // infrequent)
|
||||||
## ian (Sanctuary)
|
## ian (Sanctuary)
|
||||||
(fromDb "sagacioussuricata.com" // tech)
|
(mkText "https://sagacioussuricata.com/feed.xml" // tech // infrequent)
|
||||||
## Bunnie Juang
|
## Bunnie Juang
|
||||||
(fromDb "bunniestudios.com" // tech)
|
(mkText "https://www.bunniestudios.com/blog/?feed=rss2" // tech // infrequent)
|
||||||
(fromDb "blog.danieljanus.pl" // tech)
|
(mkText "https://blog.danieljanus.pl/atom.xml" // tech // infrequent)
|
||||||
(fromDb "ianthehenry.com" // tech)
|
(mkText "https://ianthehenry.com/feed.xml" // tech // infrequent)
|
||||||
(fromDb "bitbashing.io" // tech)
|
(mkText "https://bitbashing.io/feed.xml" // tech // infrequent)
|
||||||
(fromDb "idiomdrottning.org" // uncat)
|
(mkText "https://idiomdrottning.org/feed.xml" // uncat // daily)
|
||||||
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
|
||||||
(fromDb "jefftk.com" // tech)
|
(mkText "https://www.jefftk.com/news.rss" // tech // daily)
|
||||||
(fromDb "pomeroyb.com" // tech)
|
(mkText "https://pomeroyb.com/feed.xml" // tech // infrequent)
|
||||||
|
|
||||||
# (TECH; POL) COMMENTATORS
|
# (TECH; POL) COMMENTATORS
|
||||||
## Matt Webb -- engineering-ish, but dreamy
|
|
||||||
(fromDb "interconnected.org/home/feed" // rat)
|
|
||||||
(fromDb "edwardsnowden.substack.com" // pol // text)
|
(fromDb "edwardsnowden.substack.com" // pol // text)
|
||||||
## Julia Evans
|
|
||||||
(mkText "https://jvns.ca/atom.xml" // tech // weekly)
|
|
||||||
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
(mkText "http://benjaminrosshoffman.com/feed" // pol // weekly)
|
||||||
## Ben Thompson
|
## Ben Thompson
|
||||||
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
(mkText "https://www.stratechery.com/rss" // pol // weekly)
|
||||||
## Balaji
|
## Balaji
|
||||||
(fromDb "balajis.com" // pol)
|
(mkText "https://balajis.com/rss" // pol // weekly)
|
||||||
(fromDb "ben-evans.com/benedictevans" // pol)
|
(mkText "https://www.ben-evans.com/benedictevans/rss.xml" // pol // weekly)
|
||||||
(fromDb "lynalden.com" // pol)
|
(mkText "https://www.lynalden.com/feed" // pol // infrequent)
|
||||||
(fromDb "austinvernon.site" // tech)
|
(mkText "https://austinvernon.site/rss.xml" // tech // infrequent)
|
||||||
(mkSubstack "oversharing" // pol // daily)
|
(mkSubstack "oversharing" // pol // daily)
|
||||||
(mkSubstack "doomberg" // tech // weekly)
|
(mkSubstack "doomberg" // tech // weekly)
|
||||||
## David Rosenthal
|
## David Rosenthal
|
||||||
(fromDb "blog.dshr.org" // pol)
|
(mkText "https://blog.dshr.org/rss.xml" // pol // weekly)
|
||||||
## Matt Levine
|
## Matt Levine
|
||||||
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
|
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
|
||||||
(fromDb "stpeter.im/atom.xml" // pol)
|
(mkText "https://stpeter.im/atom.xml" // pol // weekly)
|
||||||
## Peter Saint-Andre -- side project of stpeter.im
|
|
||||||
(fromDb "philosopher.coach" // rat)
|
|
||||||
|
|
||||||
# RATIONALITY/PHILOSOPHY/ETC
|
# RATIONALITY/PHILOSOPHY/ETC
|
||||||
(mkSubstack "samkriss" // humor // infrequent)
|
(mkSubstack "samkriss" // humor // infrequent)
|
||||||
(fromDb "unintendedconsequenc.es" // rat)
|
(mkText "https://unintendedconsequenc.es/feed" // rat // infrequent)
|
||||||
(fromDb "applieddivinitystudies.com" // rat)
|
(mkText "https://applieddivinitystudies.com/atom.xml" // rat // weekly)
|
||||||
(fromDb "slimemoldtimemold.com" // rat)
|
(mkText "https://slimemoldtimemold.com/feed.xml" // rat // weekly)
|
||||||
(fromDb "richardcarrier.info" // rat)
|
(mkText "https://www.richardcarrier.info/feed" // rat // weekly)
|
||||||
(fromDb "gwern.net" // rat)
|
(mkText "https://www.gwern.net/feed.xml" // uncat // infrequent)
|
||||||
## Jason Crawford
|
## Jason Crawford
|
||||||
(fromDb "rootsofprogress.org" // rat)
|
(mkText "https://rootsofprogress.org/feed.xml" // rat // weekly)
|
||||||
## Robin Hanson
|
## Robin Hanson
|
||||||
(fromDb "overcomingbias.com" // rat)
|
(mkText "https://www.overcomingbias.com/feed" // rat // daily)
|
||||||
## Scott Alexander
|
## Scott Alexander
|
||||||
(mkSubstack "astralcodexten" // rat // daily)
|
(mkSubstack "astralcodexten" // rat // daily)
|
||||||
## Paul Christiano
|
## Paul Christiano
|
||||||
(fromDb "sideways-view.com" // rat)
|
(mkText "https://sideways-view.com/feed" // rat // infrequent)
|
||||||
## Sean Carroll
|
## Sean Carroll
|
||||||
(fromDb "preposterousuniverse.com" // rat)
|
(mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent)
|
||||||
|
|
||||||
## mostly dating topics. not advice, or humor, but looking through a social lens
|
## mostly dating topics. not advice, or humor, but looking through a social lens
|
||||||
(fromDb "putanumonit.com" // rat)
|
(mkText "https://putanumonit.com/feed" // rat // infrequent)
|
||||||
|
|
||||||
# CODE
|
# CODE
|
||||||
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
|
||||||
];
|
];
|
||||||
|
|
||||||
images = [
|
images = [
|
||||||
(fromDb "smbc-comics.com" // img // humor)
|
(mkImg "https://www.smbc-comics.com/comic/rss" // humor // daily)
|
||||||
(fromDb "xkcd.com" // img // humor)
|
(mkImg "https://xkcd.com/atom.xml" // humor // daily)
|
||||||
(fromDb "pbfcomics.com" // img // humor)
|
(mkImg "https://pbfcomics.com/feed" // humor // infrequent)
|
||||||
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
||||||
|
|
||||||
# ART
|
# ART
|
||||||
(fromDb "miniature-calendar.com" // img // art // daily)
|
(mkImg "https://miniature-calendar.com/feed" // art // daily)
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./all.nix
|
||||||
./x86_64.nix
|
./x86_64.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
@@ -1,7 +1,8 @@
|
|||||||
{ lib, pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
{
|
{
|
||||||
config = lib.mkIf (pkgs.system == "x86_64-linux") {
|
config = mkIf (pkgs.system == "x86_64-linux") {
|
||||||
boot.initrd.availableKernelModules = [
|
boot.initrd.availableKernelModules = [
|
||||||
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||||
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
@@ -1,11 +0,0 @@
|
|||||||
# Terminal UI mail client
|
|
||||||
{ config, sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
sops.secrets."aerc_accounts" = {
|
|
||||||
owner = config.users.users.colin.name;
|
|
||||||
sopsFile = ../../../secrets/universal/aerc_accounts.conf;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sane.user.fs.".config/aerc/accounts.conf" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.aerc_accounts.path;
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./aerc.nix
|
|
||||||
./firefox.nix
|
|
||||||
./gfeeds.nix
|
|
||||||
./git.nix
|
|
||||||
./gpodder.nix
|
|
||||||
./keyring.nix
|
|
||||||
./kitty.nix
|
|
||||||
./libreoffice.nix
|
|
||||||
./mime.nix
|
|
||||||
./mpv.nix
|
|
||||||
./neovim.nix
|
|
||||||
./newsflash.nix
|
|
||||||
./splatmoji.nix
|
|
||||||
./ssh.nix
|
|
||||||
./sublime-music.nix
|
|
||||||
./vlc.nix
|
|
||||||
./xdg-dirs.nix
|
|
||||||
./zsh
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,18 +0,0 @@
|
|||||||
{ lib, pkgs, sane-lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
mkCfg = lib.generators.toINI { };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.user.fs.".config/git/config" = sane-lib.fs.wantedText (mkCfg {
|
|
||||||
user.name = "Colin";
|
|
||||||
user.email = "colin@uninsane.org";
|
|
||||||
alias.co = "checkout";
|
|
||||||
# difftastic docs:
|
|
||||||
# - <https://difftastic.wilfred.me.uk/git.html>
|
|
||||||
diff.tool = "difftastic";
|
|
||||||
difftool.prompt = false;
|
|
||||||
"difftool \"difftastic\"".cmd = ''${pkgs.difftastic}/bin/difft "$LOCAL" "$REMOTE"'';
|
|
||||||
# now run `git difftool` to use difftastic git
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
{ config, sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
sane.user.persist.private = [ ".local/share/keyrings" ];
|
|
||||||
|
|
||||||
sane.user.fs."private/.local/share/keyrings/default" = {
|
|
||||||
generated.script.script = builtins.readFile ../../../scripts/init-keyring;
|
|
||||||
# TODO: is this `wantedBy` needed? can we inherit it?
|
|
||||||
wantedBy = [ config.sane.fs."/home/colin/private".unit ];
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
{ pkgs, sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
sane.user.fs.".config/kitty/kitty.conf" = sane-lib.fs.wantedText ''
|
|
||||||
# docs: https://sw.kovidgoyal.net/kitty/conf/
|
|
||||||
# disable terminal bell (when e.g. you backspace too many times)
|
|
||||||
enable_audio_bell no
|
|
||||||
|
|
||||||
map ctrl+n new_os_window_with_cwd
|
|
||||||
|
|
||||||
include ${pkgs.kitty-themes}/themes/PaperColor_dark.conf
|
|
||||||
'';
|
|
||||||
# THEME CHOICES:
|
|
||||||
# docs: https://github.com/kovidgoyal/kitty-themes
|
|
||||||
# theme = "1984 Light"; # dislike: awful, harsh blues/teals
|
|
||||||
# theme = "Adventure Time"; # dislike: harsh (dark)
|
|
||||||
# theme = "Atom One Light"; # GOOD: light theme. all color combos readable. not a huge fan of the blue.
|
|
||||||
# theme = "Belafonte Day"; # dislike: too low contrast for text colors
|
|
||||||
# theme = "Belafonte Night"; # better: dark theme that's easy on the eyes. all combos readable. low contrast.
|
|
||||||
# theme = "Catppuccin"; # dislike: a bit pale/low-contrast (dark)
|
|
||||||
# theme = "Desert"; # mediocre: colors are harsh
|
|
||||||
# theme = "Earthsong"; # BEST: dark theme. readable, good contrast. unique, but decent colors.
|
|
||||||
# theme = "Espresso Libre"; # better: dark theme. readable, but meh colors
|
|
||||||
# theme = "Forest Night"; # decent: very pastel. it's workable, but unconventional and muted/flat.
|
|
||||||
# theme = "Gruvbox Material Light Hard"; # mediocre light theme.
|
|
||||||
# theme = "kanagawabones"; # better: dark theme. colors are too background-y
|
|
||||||
# theme = "Kaolin Dark"; # dislike: too dark
|
|
||||||
# theme = "Kaolin Breeze"; # mediocre: not-too-harsh light theme, but some parts are poor contrast
|
|
||||||
# theme = "Later This Evening"; # mediocre: not-too-harsh dark theme, but cursor is poor contrast
|
|
||||||
# theme = "Material"; # decent: light theme, few colors.
|
|
||||||
# theme = "Mayukai"; # decent: not-too-harsh dark theme. the teal is a bit straining
|
|
||||||
# theme = "Nord"; # mediocre: pale background, low contrast
|
|
||||||
# theme = "One Half Light"; # better: not-too-harsh light theme. contrast could be better
|
|
||||||
# theme = "PaperColor Dark"; # BEST: dark theme, very readable still the colors are background-y
|
|
||||||
# theme = "Parasio Dark"; # dislike: too low contrast
|
|
||||||
# theme = "Pencil Light"; # better: not-too-harsh light theme. decent contrast.
|
|
||||||
# theme = "Pnevma"; # dislike: too low contrast
|
|
||||||
# theme = "Piatto Light"; # better: readable light theme. pleasing colors. powerline prompt is hard to read.
|
|
||||||
# theme = "Rosé Pine Dawn"; # GOOD: light theme. all color combinations are readable. it is very mild -- may need to manually tweak contrast. tasteful colors
|
|
||||||
# theme = "Rosé Pine Moon"; # GOOD: dark theme. tasteful colors. but background is a bit intense
|
|
||||||
# theme = "Sea Shells"; # mediocre. not all color combos are readable
|
|
||||||
# theme = "Solarized Light"; # mediocre: not-too-harsh light theme; GREAT background; but some colors are low contrast
|
|
||||||
# theme = "Solarized Dark Higher Contrast"; # better: dark theme, decent colors
|
|
||||||
# theme = "Sourcerer"; # mediocre: ugly colors
|
|
||||||
# theme = "Space Gray"; # mediocre: too muted
|
|
||||||
# theme = "Space Gray Eighties"; # better: all readable, decent colors
|
|
||||||
# theme = "Spacemacs"; # mediocre: too muted
|
|
||||||
# theme = "Spring"; # mediocre: readable light theme, but the teal is ugly.
|
|
||||||
# theme = "Srcery"; # better: highly readable. colors are ehhh
|
|
||||||
# theme = "Substrata"; # decent: nice colors, but a bit flat.
|
|
||||||
# theme = "Sundried"; # mediocre: the solar text makes me squint
|
|
||||||
# theme = "Symfonic"; # mediocre: the dark purple has low contrast to the black bg.
|
|
||||||
# theme = "Tango Light"; # dislike: teal is too grating
|
|
||||||
# theme = "Tokyo Night Day"; # medicore: too muted
|
|
||||||
# theme = "Tokyo Night"; # better: tasteful. a bit flat
|
|
||||||
# theme = "Tomorrow"; # GOOD: all color combinations are readable. contrast is slightly better than Rose. on the blander side
|
|
||||||
# theme = "Treehouse"; # dislike: the orange is harsh on my eyes.
|
|
||||||
# theme = "Urple"; # dislike: weird palette
|
|
||||||
# theme = "Warm Neon"; # decent: not-too-harsh dark theme. the green is a bit unattractive
|
|
||||||
# theme = "Wild Cherry"; # GOOD: dark theme: nice colors. a bit flat
|
|
||||||
# theme = "Xcodedark"; # dislike: bad palette
|
|
||||||
# theme = "citylights"; # decent: dark theme. some parts have just a bit low contrast
|
|
||||||
# theme = "neobones_light"; # better light theme. the background is maybe too muted
|
|
||||||
# theme = "vimbones";
|
|
||||||
# theme = "zenbones_dark"; # mediocre: readable, but meh colors
|
|
||||||
# theme = "zenbones_light"; # decent: light theme. all colors are readable. contrast is passable but not excellent. highlight color is BAD
|
|
||||||
# theme = "zenwritten_dark"; # mediocre: looks same as zenbones_dark
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
{ sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# libreoffice: disable first-run stuff
|
|
||||||
sane.user.fs.".config/libreoffice/4/user/registrymodifications.xcu" = sane-lib.fs.wantedText ''
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>
|
|
||||||
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="ShowTipOfTheDay" oor:op="fuse"><value>false</value></prop></item>
|
|
||||||
</oor:items>
|
|
||||||
'';
|
|
||||||
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeDonateShown" oor:op="fuse"><value>1667693880</value></prop></item>
|
|
||||||
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeGetInvolvedShown" oor:op="fuse"><value>1667693880</value></prop></item>
|
|
||||||
}
|
|
@@ -1,42 +0,0 @@
|
|||||||
{ config, sane-lib, ...}:
|
|
||||||
|
|
||||||
let
|
|
||||||
www = config.sane.web-browser.browser.desktop;
|
|
||||||
pdf = "org.gnome.Evince.desktop";
|
|
||||||
md = "obsidian.desktop";
|
|
||||||
thumb = "org.gnome.gThumb.desktop";
|
|
||||||
video = "vlc.desktop";
|
|
||||||
# audio = "mpv.desktop";
|
|
||||||
audio = "vlc.desktop";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
|
|
||||||
# the xdg mime type for a file can be found with:
|
|
||||||
# - `xdg-mime query filetype path/to/thing.ext`
|
|
||||||
# we can have single associations or a list of associations.
|
|
||||||
# there's also options to *remove* [non-default] associations from specific apps
|
|
||||||
xdg.mime.enable = true;
|
|
||||||
xdg.mime.defaultApplications = {
|
|
||||||
# AUDIO
|
|
||||||
"audio/flac" = audio;
|
|
||||||
"audio/mpeg" = audio;
|
|
||||||
"audio/x-vorbis+ogg" = audio;
|
|
||||||
# IMAGES
|
|
||||||
"image/heif" = thumb; # apple codec
|
|
||||||
"image/png" = thumb;
|
|
||||||
"image/jpeg" = thumb;
|
|
||||||
# VIDEO
|
|
||||||
"video/mp4" = video;
|
|
||||||
"video/quicktime" = video;
|
|
||||||
"video/x-matroska" = video;
|
|
||||||
# HTML
|
|
||||||
"text/html" = www;
|
|
||||||
"x-scheme-handler/http" = www;
|
|
||||||
"x-scheme-handler/https" = www;
|
|
||||||
"x-scheme-handler/about" = www;
|
|
||||||
"x-scheme-handler/unknown" = www;
|
|
||||||
# RICH-TEXT DOCUMENTS
|
|
||||||
"application/pdf" = pdf;
|
|
||||||
"text/markdown" = md;
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
{ sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# format is <key>=%<length>%<value>
|
|
||||||
sane.user.fs.".config/mpv/mpv.conf" = sane-lib.fs.wantedText ''
|
|
||||||
save-position-on-quit=%3%yes
|
|
||||||
keep-open=%3%yes
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
|
|
@@ -1,129 +0,0 @@
|
|||||||
{ lib, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
inherit (builtins) map;
|
|
||||||
inherit (lib) concatMapStrings optionalString;
|
|
||||||
# this structure roughly mirrors home-manager's `programs.neovim.plugins` option
|
|
||||||
plugins = with pkgs.vimPlugins; [
|
|
||||||
# docs: surround-nvim: https://github.com/ur4ltz/surround.nvim/
|
|
||||||
# docs: vim-surround: https://github.com/tpope/vim-surround
|
|
||||||
{ plugin = vim-surround; }
|
|
||||||
# docs: fzf-vim (fuzzy finder): https://github.com/junegunn/fzf.vim
|
|
||||||
{ plugin = fzf-vim; }
|
|
||||||
({
|
|
||||||
# docs: tex-conceal-vim: https://github.com/KeitaNakamura/tex-conceal.vim/
|
|
||||||
plugin = tex-conceal-vim;
|
|
||||||
type = "viml";
|
|
||||||
config = ''
|
|
||||||
" present prettier fractions
|
|
||||||
let g:tex_conceal_frac=1
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
({
|
|
||||||
plugin = vim-SyntaxRange;
|
|
||||||
type = "viml";
|
|
||||||
config = ''
|
|
||||||
" enable markdown-style codeblock highlighting for tex code
|
|
||||||
autocmd BufEnter * call SyntaxRange#Include('```tex', '```', 'tex', 'NonText')
|
|
||||||
" autocmd Syntax tex set conceallevel=2
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
({
|
|
||||||
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
|
|
||||||
# docs: https://github.com/nvim-treesitter/nvim-treesitter
|
|
||||||
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
|
|
||||||
# this is required for tree-sitter to even highlight
|
|
||||||
plugin = nvim-treesitter.withAllGrammars;
|
|
||||||
type = "lua";
|
|
||||||
config = ''
|
|
||||||
require'nvim-treesitter.configs'.setup {
|
|
||||||
highlight = {
|
|
||||||
enable = true,
|
|
||||||
-- disable treesitter on Rust so that we can use SyntaxRange
|
|
||||||
-- and leverage TeX rendering in rust projects
|
|
||||||
disable = { "rust", "tex", "latex" },
|
|
||||||
-- disable = { "tex", "latex" },
|
|
||||||
-- true to also use builtin vim syntax highlighting when treesitter fails
|
|
||||||
additional_vim_regex_highlighting = false
|
|
||||||
},
|
|
||||||
incremental_selection = {
|
|
||||||
enable = true,
|
|
||||||
keymaps = {
|
|
||||||
init_selection = "gnn",
|
|
||||||
node_incremental = "grn",
|
|
||||||
mcope_incremental = "grc",
|
|
||||||
node_decremental = "grm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
indent = {
|
|
||||||
enable = true,
|
|
||||||
disable = {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vim.o.foldmethod = 'expr'
|
|
||||||
vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
];
|
|
||||||
plugin-packages = map (p: p.plugin) plugins;
|
|
||||||
plugin-config-tex = concatMapStrings (p: optionalString (p.type or "" == "viml") p.config) plugins;
|
|
||||||
plugin-config-lua = concatMapStrings (p: optionalString (p.type or "" == "lua") p.config) plugins;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
# private because there could be sensitive things in the swap
|
|
||||||
sane.user.persist.private = [ ".cache/vim-swap" ];
|
|
||||||
|
|
||||||
programs.neovim = {
|
|
||||||
# neovim: https://github.com/neovim/neovim
|
|
||||||
enable = true;
|
|
||||||
viAlias = true;
|
|
||||||
vimAlias = true;
|
|
||||||
configure = {
|
|
||||||
packages.myVimPackage = {
|
|
||||||
start = plugin-packages;
|
|
||||||
};
|
|
||||||
customRC = ''
|
|
||||||
" let the terminal handle mouse events, that way i get OS-level ctrl+shift+c/etc
|
|
||||||
" this used to be default, until <https://github.com/neovim/neovim/pull/19290>
|
|
||||||
set mouse=
|
|
||||||
|
|
||||||
" copy/paste to system clipboard
|
|
||||||
set clipboard=unnamedplus
|
|
||||||
|
|
||||||
" screw tabs; always expand them into spaces
|
|
||||||
set expandtab
|
|
||||||
|
|
||||||
" at least don't open files with sections folded by default
|
|
||||||
set nofoldenable
|
|
||||||
|
|
||||||
" allow text substitutions for certain glyphs.
|
|
||||||
" higher number = more aggressive substitution (0, 1, 2, 3)
|
|
||||||
" i only make use of this for tex, but it's unclear how to
|
|
||||||
" apply that *just* to tex and retain the SyntaxRange stuff.
|
|
||||||
set conceallevel=2
|
|
||||||
|
|
||||||
" horizontal rule under the active line
|
|
||||||
" set cursorline
|
|
||||||
|
|
||||||
" highlight trailing space & related syntax errors (doesn't seem to work??)
|
|
||||||
" let c_space_errors=1
|
|
||||||
" let python_space_errors=1
|
|
||||||
|
|
||||||
" enable highlighting of leading/trailing spaces,
|
|
||||||
" and especially tabs
|
|
||||||
" source: https://www.reddit.com/r/neovim/comments/chlmfk/highlight_trailing_whitespaces_in_neovim/
|
|
||||||
set list
|
|
||||||
set listchars=tab:▷\·,trail:·,extends:◣,precedes:◢,nbsp:○
|
|
||||||
|
|
||||||
""""" PLUGIN CONFIG (tex)
|
|
||||||
${plugin-config-tex}
|
|
||||||
|
|
||||||
""""" PLUGIN CONFIG (lua)
|
|
||||||
lua <<EOF
|
|
||||||
${plugin-config-lua}
|
|
||||||
EOF
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
{ config, sane-lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# TODO: this should only be shipped on gui platforms
|
|
||||||
sops.secrets."sublime_music_config" = {
|
|
||||||
owner = config.users.users.colin.name;
|
|
||||||
sopsFile = ../../../secrets/universal/sublime_music_config.json.bin;
|
|
||||||
format = "binary";
|
|
||||||
};
|
|
||||||
sane.user.fs.".config/sublime-music/config.json" = sane-lib.fs.wantedSymlinkTo config.sops.secrets.sublime_music_config.path;
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
{ lib, sane-lib, ...}:
|
|
||||||
|
|
||||||
{
|
|
||||||
# XDG defines things like ~/Desktop, ~/Downloads, etc.
|
|
||||||
# these clutter the home, so i mostly don't use them.
|
|
||||||
sane.user.fs.".config/user-dirs.dirs" = sane-lib.fs.wantedText ''
|
|
||||||
XDG_DESKTOP_DIR="$HOME/.xdg/Desktop"
|
|
||||||
XDG_DOCUMENTS_DIR="$HOME/dev"
|
|
||||||
XDG_DOWNLOAD_DIR="$HOME/tmp"
|
|
||||||
XDG_MUSIC_DIR="$HOME/Music"
|
|
||||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
|
||||||
XDG_PUBLICSHARE_DIR="$HOME/.xdg/Public"
|
|
||||||
XDG_TEMPLATES_DIR="$HOME/.xdg/Templates"
|
|
||||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
|
||||||
'';
|
|
||||||
|
|
||||||
# prevent `xdg-user-dirs-update` from overriding/updating our config
|
|
||||||
# see <https://manpages.ubuntu.com/manpages/bionic/man5/user-dirs.conf.5.html>
|
|
||||||
sane.user.fs.".config/user-dirs.conf" = sane-lib.fs.wantedText "enabled=False";
|
|
||||||
}
|
|
@@ -1,143 +0,0 @@
|
|||||||
{ pkgs, sane-lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
# powerlevel10k prompt config
|
|
||||||
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
|
|
||||||
p10k-overrides = ''
|
|
||||||
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
|
|
||||||
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
|
|
||||||
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
|
|
||||||
# i can disable gitstatusd and get slower fallback git queries:
|
|
||||||
# - either universally
|
|
||||||
# - or selectively by path
|
|
||||||
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
|
|
||||||
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
|
|
||||||
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
|
|
||||||
|
|
||||||
# show user@host also when logged into the current machine.
|
|
||||||
# default behavior is to show it only over ssh.
|
|
||||||
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
|
|
||||||
'';
|
|
||||||
|
|
||||||
prezto-init = ''
|
|
||||||
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
|
|
||||||
source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
|
|
||||||
source ${pkgs.zsh-prezto}/share/zsh-prezto/init.zsh
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
sane.user.persist.plaintext = [
|
|
||||||
# we don't need to full zsh dir -- just the history file --
|
|
||||||
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
|
|
||||||
# TODO: should be private?
|
|
||||||
".local/share/zsh"
|
|
||||||
# cache gitstatus otherwise p10k fetched it from the net EVERY BOOT
|
|
||||||
".cache/gitstatus"
|
|
||||||
];
|
|
||||||
|
|
||||||
# zsh/prezto complains if zshrc doesn't exist; but it does allow an "empty" file.
|
|
||||||
sane.user.fs.".config/zsh/.zshrc" = sane-lib.fs.wantedText "# ";
|
|
||||||
|
|
||||||
# enable zsh completions
|
|
||||||
environment.pathsToLink = [ "/share/zsh" ];
|
|
||||||
|
|
||||||
programs.zsh = {
|
|
||||||
enable = true;
|
|
||||||
histFile = "$HOME/.local/share/zsh/history";
|
|
||||||
shellAliases = {
|
|
||||||
":q" = "exit";
|
|
||||||
# common typos
|
|
||||||
"cd.." = "cd ..";
|
|
||||||
"cd../" = "cd ../";
|
|
||||||
};
|
|
||||||
setOptions = [
|
|
||||||
# defaults:
|
|
||||||
"HIST_IGNORE_DUPS"
|
|
||||||
"SHARE_HISTORY"
|
|
||||||
"HIST_FCNTL_LOCK"
|
|
||||||
# disable `rm *` confirmations
|
|
||||||
"rmstarsilent"
|
|
||||||
];
|
|
||||||
|
|
||||||
# .zshenv config:
|
|
||||||
shellInit = ''
|
|
||||||
ZDOTDIR=$HOME/.config/zsh
|
|
||||||
'';
|
|
||||||
|
|
||||||
# .zshrc config:
|
|
||||||
interactiveShellInit =
|
|
||||||
(builtins.readFile ./p10k.zsh)
|
|
||||||
+ p10k-overrides
|
|
||||||
+ prezto-init
|
|
||||||
+ ''
|
|
||||||
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
|
|
||||||
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
|
|
||||||
autoload -Uz zmv
|
|
||||||
|
|
||||||
HISTORY_IGNORE='(sane-shutdown *|sane-reboot *|rm *)'
|
|
||||||
|
|
||||||
# extra aliases
|
|
||||||
# TODO: move to `shellAliases` config?
|
|
||||||
function nd() {
|
|
||||||
mkdir -p "$1";
|
|
||||||
pushd "$1";
|
|
||||||
}
|
|
||||||
|
|
||||||
# auto-cd into any of these dirs by typing them and pressing 'enter':
|
|
||||||
hash -d 3rd="/home/colin/dev/3rd"
|
|
||||||
hash -d dev="/home/colin/dev"
|
|
||||||
hash -d knowledge="/home/colin/knowledge"
|
|
||||||
hash -d nixos="/home/colin/nixos"
|
|
||||||
hash -d nixpkgs="/home/colin/dev/3rd/nixpkgs"
|
|
||||||
hash -d ref="/home/colin/ref"
|
|
||||||
hash -d secrets="/home/colin/knowledge/secrets"
|
|
||||||
hash -d tmp="/home/colin/tmp"
|
|
||||||
hash -d uninsane="/home/colin/dev/uninsane"
|
|
||||||
hash -d Videos="/home/colin/Videos"
|
|
||||||
'';
|
|
||||||
|
|
||||||
syntaxHighlighting.enable = true;
|
|
||||||
vteIntegration = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# enable a command-not-found hook to show nix packages that might provide the binary typed.
|
|
||||||
programs.nix-index.enable = true;
|
|
||||||
programs.command-not-found.enable = false; #< mutually exclusive with nix-index
|
|
||||||
|
|
||||||
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
|
|
||||||
# see: https://github.com/sorin-ionescu/prezto
|
|
||||||
# i believe this file is auto-sourced by the prezto init.zsh script.
|
|
||||||
sane.user.fs.".config/zsh/.zpreztorc" = sane-lib.fs.wantedText ''
|
|
||||||
zstyle ':prezto:*:*' color 'yes'
|
|
||||||
|
|
||||||
# modules (they ship with prezto):
|
|
||||||
# ENVIRONMENT: configures jobs to persist after shell exit; other basic niceties
|
|
||||||
# TERMINAL: auto-titles terminal (e.g. based on cwd)
|
|
||||||
# EDITOR: configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
|
|
||||||
# HISTORY: `history-stat` alias, setopts for good history defaults
|
|
||||||
# DIRECTORY: sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack
|
|
||||||
# SPECTRUM: helpers for term colors and styling. used by prompts? might be unnecessary
|
|
||||||
# UTILITY: configures aliases like `ll`, `la`, disables globbing for things like rsync
|
|
||||||
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
|
|
||||||
# COMPLETION: tab completion. requires `utility` module prior to loading
|
|
||||||
# TODO: enable AUTO_PARAM_SLASH
|
|
||||||
zstyle ':prezto:load' pmodule \
|
|
||||||
'environment' \
|
|
||||||
'terminal' \
|
|
||||||
'editor' \
|
|
||||||
'history' \
|
|
||||||
'directory' \
|
|
||||||
'spectrum' \
|
|
||||||
'utility' \
|
|
||||||
'completion' \
|
|
||||||
'prompt'
|
|
||||||
|
|
||||||
# default keymap. try also `vicmd` (vim normal mode, AKA "cmd mode") or `vi`.
|
|
||||||
zstyle ':prezto:module:editor' key-bindings 'emacs'
|
|
||||||
|
|
||||||
zstyle ':prezto:module:prompt' theme 'powerlevel10k'
|
|
||||||
|
|
||||||
# disable `mv` confirmation (and `rm`, too, unfortunately)
|
|
||||||
zstyle ':prezto:module:utility' safe-ops 'no'
|
|
||||||
'';
|
|
||||||
}
|
|
@@ -1,4 +1,4 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
# services.i2p.enable = true;
|
services.i2p.enable = true;
|
||||||
}
|
}
|
||||||
|
@@ -21,12 +21,6 @@
|
|||||||
sane.ids.freshrss.uid = 2401;
|
sane.ids.freshrss.uid = 2401;
|
||||||
sane.ids.freshrss.gid = 2401;
|
sane.ids.freshrss.gid = 2401;
|
||||||
sane.ids.mediawiki.uid = 2402;
|
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.colin.uid = 1000;
|
||||||
sane.ids.guest.uid = 1100;
|
sane.ids.guest.uid = 1100;
|
||||||
|
@@ -1,6 +1,16 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ 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".
|
# the default backend is "wpa_supplicant".
|
||||||
# wpa_supplicant reliably picks weak APs to connect to.
|
# wpa_supplicant reliably picks weak APs to connect to.
|
||||||
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
||||||
@@ -20,4 +30,14 @@
|
|||||||
General.RoamThreshold = "-52"; # default -70
|
General.RoamThreshold = "-52"; # default -70
|
||||||
General.RoamThreshold5G = "-52"; # default -76
|
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,16 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
sane.persist.stores.private.origin = "/home/colin/private";
|
|
||||||
# store /home/colin/a/b in /home/private/a/b instead of /home/private/home/colin/a/b
|
|
||||||
sane.persist.stores.private.prefix = "/home/colin";
|
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
|
||||||
"/var/log"
|
|
||||||
"/var/backup" # for e.g. postgres dumps
|
|
||||||
# TODO: move elsewhere
|
|
||||||
"/var/lib/alsa" # preserve output levels, default devices
|
|
||||||
"/var/lib/colord" # preserve color calibrations (?)
|
|
||||||
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,33 +1,24 @@
|
|||||||
{ config, lib, sane-data, sane-lib, ... }:
|
{ config, lib, sane-data, sane-lib, ... }:
|
||||||
|
|
||||||
let
|
|
||||||
inherit (builtins) head map mapAttrs tail;
|
|
||||||
inherit (lib) concatStringsSep mkMerge reverseList;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
sane.ssh.pubkeys =
|
sane.ssh.pubkeys =
|
||||||
let
|
let
|
||||||
# path is a DNS-style path like [ "org" "uninsane" "root" ]
|
# path is a DNS-style path like [ "org" "uninsane" "root" ]
|
||||||
keyNameForPath = path:
|
keyNameForPath = path:
|
||||||
let
|
let
|
||||||
rev = reverseList path;
|
rev = lib.reverseList path;
|
||||||
name = head rev;
|
name = builtins.head rev;
|
||||||
host = concatStringsSep "." (tail rev);
|
host = lib.concatStringsSep "." (builtins.tail rev);
|
||||||
in
|
in
|
||||||
"${name}@${host}";
|
"${name}@${host}";
|
||||||
|
|
||||||
# [{ path :: [String], value :: String }] for the keys we want to install
|
# [{ path :: [String], value :: String }] for the keys we want to install
|
||||||
globalKeys = sane-lib.flattenAttrs sane-data.keys;
|
globalKeys = sane-lib.flattenAttrs sane-data.keys;
|
||||||
domainKeys = sane-lib.flattenAttrs (
|
localKeys = sane-lib.flattenAttrs sane-data.keys.org.uninsane.local;
|
||||||
mapAttrs (host: cfg: {
|
in lib.mkMerge (builtins.map
|
||||||
colin = cfg.ssh.user_pubkey;
|
|
||||||
root = cfg.ssh.host_pubkey;
|
|
||||||
}) config.sane.hosts.by-name
|
|
||||||
);
|
|
||||||
in mkMerge (map
|
|
||||||
({ path, value }: {
|
({ path, value }: {
|
||||||
"${keyNameForPath path}" = lib.mkIf (value != null) value;
|
"${keyNameForPath path}" = value;
|
||||||
})
|
})
|
||||||
(globalKeys ++ domainKeys)
|
(globalKeys ++ localKeys)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,12 @@
|
|||||||
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
||||||
with lib;
|
with lib;
|
||||||
let
|
let
|
||||||
cfg = config.sane.guest;
|
cfg = config.sane.users;
|
||||||
fs = sane-lib.fs;
|
fs = sane-lib.fs;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
sane.guest.enable = mkOption {
|
sane.users.guest.enable = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
@@ -49,8 +49,6 @@ in
|
|||||||
|
|
||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
|
|
||||||
packages = builtins.map (p: p.pkg) config.sane.packages.enabledUserPkgs;
|
|
||||||
|
|
||||||
# mount encrypted stuff at login
|
# mount encrypted stuff at login
|
||||||
# some other nix pam users:
|
# some other nix pam users:
|
||||||
# - <https://github.com/g00pix/nixconf/blob/32c04f6fa843fed97639dd3f09e157668d3eea1f/profiles/sshfs.nix>
|
# - <https://github.com/g00pix/nixconf/blob/32c04f6fa843fed97639dd3f09e157668d3eea1f/profiles/sshfs.nix>
|
||||||
@@ -68,7 +66,6 @@ in
|
|||||||
|
|
||||||
security.pam.mount.enable = true;
|
security.pam.mount.enable = true;
|
||||||
|
|
||||||
sane.users.colin.default = true;
|
|
||||||
# ensure ~ perms are known to sane.fs module.
|
# ensure ~ perms are known to sane.fs module.
|
||||||
# TODO: this is generic enough to be lifted up into sane.fs itself.
|
# TODO: this is generic enough to be lifted up into sane.fs itself.
|
||||||
sane.fs."/home/colin".dir.acl = {
|
sane.fs."/home/colin".dir.acl = {
|
||||||
@@ -77,7 +74,7 @@ in
|
|||||||
mode = config.users.users.colin.homeMode;
|
mode = config.users.users.colin.homeMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.user.persist.plaintext = [
|
sane.persist.home.plaintext = [
|
||||||
"archive"
|
"archive"
|
||||||
"dev"
|
"dev"
|
||||||
# TODO: records should be private
|
# TODO: records should be private
|
||||||
@@ -90,26 +87,25 @@ in
|
|||||||
"Videos"
|
"Videos"
|
||||||
|
|
||||||
".cache/nix"
|
".cache/nix"
|
||||||
".cache/nix-index"
|
|
||||||
".cargo"
|
".cargo"
|
||||||
".rustup"
|
".rustup"
|
||||||
];
|
];
|
||||||
|
|
||||||
# convenience
|
# convenience
|
||||||
sane.user.fs."knowledge" = fs.wantedSymlinkTo "private/knowledge";
|
sane.fs."/home/colin/knowledge" = fs.wantedSymlinkTo "/home/colin/private/knowledge";
|
||||||
sane.user.fs."nixos" = fs.wantedSymlinkTo "dev/nixos";
|
sane.fs."/home/colin/nixos" = fs.wantedSymlinkTo "/home/colin/dev/nixos";
|
||||||
sane.user.fs."Videos/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Videos";
|
sane.fs."/home/colin/Videos/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Videos";
|
||||||
sane.user.fs."Videos/servo-incomplete" = fs.wantedSymlinkTo "/mnt/servo-media/incomplete";
|
sane.fs."/home/colin/Videos/servo-incomplete" = fs.wantedSymlinkTo "/mnt/servo-media/incomplete";
|
||||||
sane.user.fs."Music/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Music";
|
sane.fs."/home/colin/Music/servo" = fs.wantedSymlinkTo "/mnt/servo-media/Music";
|
||||||
|
|
||||||
# used by password managers, e.g. unix `pass`
|
# used by password managers, e.g. unix `pass`
|
||||||
sane.user.fs.".password-store" = fs.wantedSymlinkTo "knowledge/secrets/accounts";
|
sane.fs."/home/colin/.password-store" = fs.wantedSymlinkTo "/home/colin/knowledge/secrets/accounts";
|
||||||
|
|
||||||
sane.persist.sys.plaintext = mkIf cfg.enable [
|
sane.persist.sys.plaintext = mkIf cfg.guest.enable [
|
||||||
# intentionally allow other users to write to the guest folder
|
# intentionally allow other users to write to the guest folder
|
||||||
{ directory = "/home/guest"; user = "guest"; group = "users"; mode = "0775"; }
|
{ directory = "/home/guest"; user = "guest"; group = "users"; mode = "0775"; }
|
||||||
];
|
];
|
||||||
users.users.guest = mkIf cfg.enable {
|
users.users.guest = mkIf cfg.guest.enable {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
home = "/home/guest";
|
home = "/home/guest";
|
||||||
subUidRanges = [
|
subUidRanges = [
|
||||||
@@ -130,8 +126,8 @@ in
|
|||||||
|
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.PermitRootLogin = "no";
|
permitRootLogin = "no";
|
||||||
settings.PasswordAuthentication = false;
|
passwordAuthentication = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -6,16 +6,12 @@
|
|||||||
|
|
||||||
# sane.packages.enableDevPkgs = true;
|
# sane.packages.enableDevPkgs = true;
|
||||||
|
|
||||||
sane.roles.client = true;
|
sane.gui.sway.enable = 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.duplicity.enable = true;
|
||||||
sane.services.nixserve.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.persist.enable = true;
|
||||||
|
|
||||||
sane.gui.sway.enable = true;
|
|
||||||
|
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
@@ -23,7 +19,7 @@
|
|||||||
services.usbmuxd.enable = true;
|
services.usbmuxd.enable = true;
|
||||||
|
|
||||||
sops.secrets.colin-passwd = {
|
sops.secrets.colin-passwd = {
|
||||||
sopsFile = ../../../secrets/desko.yaml;
|
sopsFile = ../../secrets/desko.yaml;
|
||||||
neededForUsers = true;
|
neededForUsers = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,7 +41,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
sops.secrets.duplicity_passphrase = {
|
sops.secrets.duplicity_passphrase = {
|
||||||
sopsFile = ../../../secrets/desko.yaml;
|
sopsFile = ../../secrets/desko.yaml;
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.steam = {
|
programs.steam = {
|
||||||
@@ -54,7 +50,7 @@
|
|||||||
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
|
||||||
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
|
||||||
};
|
};
|
||||||
sane.user.persist.plaintext = [
|
sane.persist.home.plaintext = [
|
||||||
".steam"
|
".steam"
|
||||||
".local/share/Steam"
|
".local/share/Steam"
|
||||||
];
|
];
|
@@ -1,16 +1,12 @@
|
|||||||
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||||
|
|
||||||
# args from flake-level `import`
|
|
||||||
{ hostName, localSystem }:
|
{ hostName, localSystem }:
|
||||||
|
{ ... }:
|
||||||
# module args
|
|
||||||
{ config, ... }:
|
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./by-name/${hostName}
|
./${hostName}
|
||||||
./common
|
./common
|
||||||
./modules
|
|
||||||
];
|
];
|
||||||
|
|
||||||
networking.hostName = hostName;
|
networking.hostName = hostName;
|
||||||
|
@@ -1,16 +1,12 @@
|
|||||||
{ config, pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./fs.nix
|
./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.packages.enableDevPkgs = true;
|
||||||
|
|
||||||
# sane.guest.enable = true;
|
# sane.users.guest.enable = true;
|
||||||
sane.gui.sway.enable = true;
|
sane.gui.sway.enable = true;
|
||||||
sane.persist.enable = true;
|
sane.persist.enable = true;
|
||||||
sane.nixcache.enable = true;
|
sane.nixcache.enable = true;
|
||||||
@@ -18,7 +14,7 @@
|
|||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||||
|
|
||||||
sops.secrets.colin-passwd = {
|
sops.secrets.colin-passwd = {
|
||||||
sopsFile = ../../../secrets/lappy.yaml;
|
sopsFile = ../../secrets/lappy.yaml;
|
||||||
neededForUsers = true;
|
neededForUsers = true;
|
||||||
};
|
};
|
||||||
|
|
@@ -6,10 +6,6 @@
|
|||||||
./kernel.nix
|
./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*.
|
# cross-compiled documentation is *slow*.
|
||||||
# no obvious way to natively compile docs (2022/09/29).
|
# no obvious way to natively compile docs (2022/09/29).
|
||||||
# entrypoint is nixos/modules/misc/documentation.nix
|
# entrypoint is nixos/modules/misc/documentation.nix
|
||||||
@@ -23,21 +19,14 @@
|
|||||||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||||
|
|
||||||
sops.secrets.colin-passwd = {
|
sops.secrets.colin-passwd = {
|
||||||
sopsFile = ../../../secrets/moby.yaml;
|
sopsFile = ../../secrets/moby.yaml;
|
||||||
neededForUsers = true;
|
neededForUsers = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.web-browser = {
|
# usability compromises
|
||||||
# compromise impermanence for the sake of usability
|
sane.web-browser.persistCache = "private";
|
||||||
persistCache = "private";
|
sane.web-browser.persistData = "private";
|
||||||
persistData = "private";
|
sane.persist.home.plaintext = [
|
||||||
|
|
||||||
# i don't do crypto stuff on moby
|
|
||||||
addons.ether-metamask.enable = false;
|
|
||||||
# addons.sideberry.enable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
sane.user.persist.plaintext = [
|
|
||||||
".config/pulse" # persist pulseaudio volume
|
".config/pulse" # persist pulseaudio volume
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -65,10 +54,9 @@
|
|||||||
|
|
||||||
# without this some GUI apps fail: `DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory`
|
# 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.
|
# this is because they can't allocate enough video ram.
|
||||||
# the default CMA seems to be 32M.
|
# the default CMA seems to be 32M. we could probably get by with as little as 64M, and safely with 128M.
|
||||||
# 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.
|
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||||
boot.kernelParams = [ "cma=512M" ];
|
boot.kernelParams = [ "cma=256M" ];
|
||||||
|
|
||||||
# mobile-nixos' /lib/firmware includes:
|
# mobile-nixos' /lib/firmware includes:
|
||||||
# rtl_bt (bluetooth)
|
# rtl_bt (bluetooth)
|
@@ -1,15 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./derived-secrets.nix
|
|
||||||
./gui
|
|
||||||
./hardware
|
|
||||||
./hostnames.nix
|
|
||||||
./hosts.nix
|
|
||||||
./nixcache.nix
|
|
||||||
./roles
|
|
||||||
./services
|
|
||||||
./wg-home.nix
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
{ 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;
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,642 +0,0 @@
|
|||||||
{ config, lib, pkgs, sane-lib, ... }:
|
|
||||||
|
|
||||||
# docs: https://nixos.wiki/wiki/Sway
|
|
||||||
with lib;
|
|
||||||
let
|
|
||||||
cfg = config.sane.gui.sway;
|
|
||||||
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
|
||||||
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
|
||||||
waybar-config = [
|
|
||||||
{ # TOP BAR
|
|
||||||
layer = "top";
|
|
||||||
height = 40;
|
|
||||||
modules-left = ["sway/workspaces" "sway/mode"];
|
|
||||||
modules-center = ["sway/window"];
|
|
||||||
modules-right = ["custom/mediaplayer" "clock" "battery" "cpu" "network"];
|
|
||||||
"sway/window" = {
|
|
||||||
max-length = 50;
|
|
||||||
};
|
|
||||||
# include song artist/title. source: https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/
|
|
||||||
"custom/mediaplayer" = {
|
|
||||||
exec = pkgs.writeShellScript "waybar-mediaplayer" ''
|
|
||||||
player_status=$(${pkgs.playerctl}/bin/playerctl status 2> /dev/null)
|
|
||||||
if [ "$player_status" = "Playing" ]; then
|
|
||||||
echo "$(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
|
||||||
elif [ "$player_status" = "Paused" ]; then
|
|
||||||
echo " $(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
interval = 2;
|
|
||||||
format = "{} ";
|
|
||||||
# return-type = "json";
|
|
||||||
on-click = "${pkgs.playerctl}/bin/playerctl play-pause";
|
|
||||||
on-scroll-up = "${pkgs.playerctl}/bin/playerctl next";
|
|
||||||
on-scroll-down = "${pkgs.playerctl}/bin/playerctl previous";
|
|
||||||
};
|
|
||||||
network = {
|
|
||||||
# docs: https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd
|
|
||||||
interval = 2;
|
|
||||||
max-length = 40;
|
|
||||||
# custom :> format specifier explained here: https://github.com/Alexays/Waybar/pull/472
|
|
||||||
format-ethernet = " {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
|
|
||||||
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
|
|
||||||
format-disconnected = "";
|
|
||||||
};
|
|
||||||
cpu = {
|
|
||||||
format = " {usage:2}%";
|
|
||||||
tooltip = false;
|
|
||||||
};
|
|
||||||
battery = {
|
|
||||||
states = {
|
|
||||||
good = 95;
|
|
||||||
warning = 30;
|
|
||||||
critical = 10;
|
|
||||||
};
|
|
||||||
format = "{icon} {capacity}%";
|
|
||||||
format-icons = [
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
];
|
|
||||||
};
|
|
||||||
clock = {
|
|
||||||
format-alt = "{:%a, %d. %b %H:%M}";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
# waybar-config-text = lib.generators.toJSON {} waybar-config;
|
|
||||||
waybar-config-text = (pkgs.formats.json {}).generate "waybar-config.json" waybar-config;
|
|
||||||
|
|
||||||
# bare sway launcher
|
|
||||||
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
|
|
||||||
${pkgs.sway}/bin/sway --debug > /tmp/sway.log 2>&1
|
|
||||||
'';
|
|
||||||
# start sway and have it construct the gtkgreeter
|
|
||||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
|
||||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /tmp/sway-as-greeter.log 2>&1
|
|
||||||
'';
|
|
||||||
# (config file for the above)
|
|
||||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
|
||||||
exec "${gtkgreet-launcher}"
|
|
||||||
'';
|
|
||||||
# gtkgreet which launches a layered sway instance
|
|
||||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
|
||||||
# NB: the "command" field here is run in the user's shell.
|
|
||||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
|
||||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
|
|
||||||
'';
|
|
||||||
greeter-session = {
|
|
||||||
# greeter session config
|
|
||||||
command = "${sway-as-greeter}/bin/sway-as-greeter";
|
|
||||||
# alternatives:
|
|
||||||
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
|
||||||
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
|
||||||
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
|
||||||
};
|
|
||||||
greeterless-session = {
|
|
||||||
# no greeter
|
|
||||||
command = "${sway-launcher}/bin/sway-launcher";
|
|
||||||
user = "colin";
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
sane.gui.sway.enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.bool;
|
|
||||||
};
|
|
||||||
sane.gui.sway.useGreeter = mkOption {
|
|
||||||
description = ''
|
|
||||||
launch sway via a greeter (like greetd's gtkgreet).
|
|
||||||
sway is usable without a greeter, but skipping the greeter means no PAM session.
|
|
||||||
'';
|
|
||||||
default = true;
|
|
||||||
type = types.bool;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = mkIf cfg.enable {
|
|
||||||
sane.gui.enable = true;
|
|
||||||
|
|
||||||
# swap in these lines to use SDDM instead of `services.greetd`.
|
|
||||||
# services.xserver.displayManager.sddm.enable = true;
|
|
||||||
# services.xserver.enable = true;
|
|
||||||
services.greetd = {
|
|
||||||
# greetd source/docs:
|
|
||||||
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# we need the greeter's command to be on our PATH
|
|
||||||
users.users.colin.packages = [ sway-launcher ];
|
|
||||||
|
|
||||||
# some programs (e.g. fractal) **require** a "Secret Service Provider"
|
|
||||||
services.gnome.gnome-keyring.enable = true;
|
|
||||||
|
|
||||||
# unlike other DEs, sway configures no audio stack
|
|
||||||
# administer with pw-cli, pw-mon, pw-top commands
|
|
||||||
services.pipewire = {
|
|
||||||
enable = true;
|
|
||||||
alsa.enable = true;
|
|
||||||
alsa.support32Bit = true; # ??
|
|
||||||
pulse.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.useDHCP = false;
|
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
networking.wireless.enable = lib.mkForce false;
|
|
||||||
|
|
||||||
hardware.bluetooth.enable = true;
|
|
||||||
services.blueman.enable = true;
|
|
||||||
# gsd provides Rfkill, which is required for the bluetooth pane in gnome-control-center to work
|
|
||||||
services.gnome.gnome-settings-daemon.enable = true;
|
|
||||||
# start the components of gsd we need at login
|
|
||||||
systemd.user.targets."org.gnome.SettingsDaemon.Rfkill".wantedBy = [ "graphical-session.target" ];
|
|
||||||
# go ahead and `systemctl --user cat gnome-session-initialized.target`. i dare you.
|
|
||||||
# the only way i can figure out how to get Rfkill to actually load is to just disable all the shit it depends on.
|
|
||||||
# it doesn't actually seem to need ANY of them in the first place T_T
|
|
||||||
systemd.user.targets."gnome-session-initialized".enable = false;
|
|
||||||
# bluez can't connect to audio devices unless pipewire is running.
|
|
||||||
# a system service can't depend on a user service, so just launch it at graphical-session
|
|
||||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
programs.sway = {
|
|
||||||
enable = true;
|
|
||||||
wrapperFeatures.gtk = true;
|
|
||||||
};
|
|
||||||
sane.user.fs.".config/sway/config" =
|
|
||||||
let
|
|
||||||
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
|
||||||
sed = "${pkgs.gnused}/bin/sed";
|
|
||||||
wtype = "${pkgs.wtype}/bin/wtype";
|
|
||||||
kitty = "${pkgs.kitty}/bin/kitty";
|
|
||||||
launcher-cmd = fuzzel;
|
|
||||||
terminal-cmd = kitty;
|
|
||||||
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
|
||||||
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
|
||||||
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
|
||||||
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
|
||||||
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
|
||||||
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
|
||||||
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
|
||||||
# "bookmarking"/snippets inspired by Luke Smith:
|
|
||||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
|
||||||
snip-file = ./snippets.txt;
|
|
||||||
# TODO: querying sops here breaks encapsulation
|
|
||||||
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
|
|
||||||
strip-comments = "${sed} 's/ #.*$//'";
|
|
||||||
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
|
|
||||||
# TODO: next splatmoji release should allow `-s none` to disable skin tones
|
|
||||||
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
|
||||||
in sane-lib.fs.wantedText ''
|
|
||||||
### default font
|
|
||||||
font pango:monospace 8
|
|
||||||
|
|
||||||
### pixel boundary between windows
|
|
||||||
default_border pixel 3
|
|
||||||
default_floating_border pixel 2
|
|
||||||
hide_edge_borders smart
|
|
||||||
|
|
||||||
### defaults
|
|
||||||
focus_wrapping no
|
|
||||||
focus_follows_mouse yes
|
|
||||||
focus_on_window_activation smart
|
|
||||||
mouse_warping output
|
|
||||||
workspace_layout default
|
|
||||||
workspace_auto_back_and_forth no
|
|
||||||
|
|
||||||
### default colors (#border #background #text #indicator #childBorder)
|
|
||||||
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
|
|
||||||
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
|
|
||||||
client.unfocused #333333 #222222 #888888 #292d2e #222222
|
|
||||||
client.urgent #2f343a #900000 #ffffff #900000 #900000
|
|
||||||
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
|
|
||||||
client.background #ffffff
|
|
||||||
|
|
||||||
### key bindings
|
|
||||||
floating_modifier Mod1
|
|
||||||
## media keys
|
|
||||||
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
|
|
||||||
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
|
|
||||||
bindsym Mod1+Page_Up exec ${vol-up-cmd}
|
|
||||||
bindsym Mod1+Page_Down exec ${vol-down-cmd}
|
|
||||||
bindsym XF86AudioMute exec ${mute-cmd}
|
|
||||||
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
|
|
||||||
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
|
|
||||||
## special functions
|
|
||||||
bindsym Mod1+Print exec ${screenshot-cmd}
|
|
||||||
bindsym Mod1+l exec ${lock-cmd}
|
|
||||||
bindsym Mod1+s exec ${snip-cmd}
|
|
||||||
bindsym Mod1+slash exec ${emoji-cmd}
|
|
||||||
bindsym Mod1+d exec ${launcher-cmd}
|
|
||||||
bindsym Mod1+Return exec ${terminal-cmd}
|
|
||||||
bindsym Mod1+Shift+q kill
|
|
||||||
bindsym Mod1+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
|
||||||
bindsym Mod1+Shift+c reload
|
|
||||||
## layout
|
|
||||||
bindsym Mod1+b splith
|
|
||||||
bindsym Mod1+v splitv
|
|
||||||
bindsym Mod1+f fullscreen toggle
|
|
||||||
bindsym Mod1+a focus parent
|
|
||||||
bindsym Mod1+w layout tabbed
|
|
||||||
bindsym Mod1+e layout toggle split
|
|
||||||
bindsym Mod1+Shift+space floating toggle
|
|
||||||
bindsym Mod1+space focus mode_toggle
|
|
||||||
bindsym Mod1+r mode resize
|
|
||||||
## movement
|
|
||||||
bindsym Mod1+Up focus up
|
|
||||||
bindsym Mod1+Down focus down
|
|
||||||
bindsym Mod1+Left focus left
|
|
||||||
bindsym Mod1+Right focus right
|
|
||||||
bindsym Mod1+Shift+Up move up
|
|
||||||
bindsym Mod1+Shift+Down move down
|
|
||||||
bindsym Mod1+Shift+Left move left
|
|
||||||
bindsym Mod1+Shift+Right move right
|
|
||||||
## workspaces
|
|
||||||
bindsym Mod1+1 workspace number 1
|
|
||||||
bindsym Mod1+2 workspace number 2
|
|
||||||
bindsym Mod1+3 workspace number 3
|
|
||||||
bindsym Mod1+4 workspace number 4
|
|
||||||
bindsym Mod1+5 workspace number 5
|
|
||||||
bindsym Mod1+6 workspace number 6
|
|
||||||
bindsym Mod1+7 workspace number 7
|
|
||||||
bindsym Mod1+8 workspace number 8
|
|
||||||
bindsym Mod1+9 workspace number 9
|
|
||||||
bindsym Mod1+Shift+1 move container to workspace number 1
|
|
||||||
bindsym Mod1+Shift+2 move container to workspace number 2
|
|
||||||
bindsym Mod1+Shift+3 move container to workspace number 3
|
|
||||||
bindsym Mod1+Shift+4 move container to workspace number 4
|
|
||||||
bindsym Mod1+Shift+5 move container to workspace number 5
|
|
||||||
bindsym Mod1+Shift+6 move container to workspace number 6
|
|
||||||
bindsym Mod1+Shift+7 move container to workspace number 7
|
|
||||||
bindsym Mod1+Shift+8 move container to workspace number 8
|
|
||||||
bindsym Mod1+Shift+9 move container to workspace number 9
|
|
||||||
## "scratchpad" = ??
|
|
||||||
bindsym Mod1+Shift+minus move scratchpad
|
|
||||||
bindsym Mod1+minus scratchpad show
|
|
||||||
|
|
||||||
### defaults
|
|
||||||
mode "resize" {
|
|
||||||
bindsym Down resize grow height 10 px
|
|
||||||
bindsym Escape mode default
|
|
||||||
bindsym Left resize shrink width 10 px
|
|
||||||
bindsym Return mode default
|
|
||||||
bindsym Right resize grow width 10 px
|
|
||||||
bindsym Up resize shrink height 10 px
|
|
||||||
bindsym h resize shrink width 10 px
|
|
||||||
bindsym j resize grow height 10 px
|
|
||||||
bindsym k resize shrink height 10 px
|
|
||||||
bindsym l resize grow width 10 px
|
|
||||||
}
|
|
||||||
|
|
||||||
### lightly modified bars
|
|
||||||
bar {
|
|
||||||
# TODO: fonts was:
|
|
||||||
# config.fonts.fontconfig.defaultFonts; (monospace ++ emoji)
|
|
||||||
font pango:Hack, Font Awesome 6 Free, Twitter Color Emoji 24.000000
|
|
||||||
mode dock
|
|
||||||
hidden_state hide
|
|
||||||
position top
|
|
||||||
status_command ${pkgs.i3status}/bin/i3status
|
|
||||||
swaybar_command ${pkgs.waybar}/bin/waybar
|
|
||||||
workspace_buttons yes
|
|
||||||
strip_workspace_numbers no
|
|
||||||
tray_output primary
|
|
||||||
colors {
|
|
||||||
background #000000
|
|
||||||
statusline #ffffff
|
|
||||||
separator #666666
|
|
||||||
# #border #background #text
|
|
||||||
focused_workspace #4c7899 #285577 #ffffff
|
|
||||||
active_workspace #333333 #5f676a #ffffff
|
|
||||||
inactive_workspace #333333 #222222 #888888
|
|
||||||
urgent_workspace #2f343a #900000 #ffffff
|
|
||||||
binding_mode #2f343a #900000 #ffffff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
### displays
|
|
||||||
## DESKTOP
|
|
||||||
output "Samsung Electric Company S22C300 0x00007F35" {
|
|
||||||
pos 0,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
|
|
||||||
pos 1920,0
|
|
||||||
res 3440x1440
|
|
||||||
}
|
|
||||||
|
|
||||||
## LAPTOP
|
|
||||||
# sh/en TV
|
|
||||||
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
|
|
||||||
pos 0,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
# internal display
|
|
||||||
output "Unknown 0x0637 0x00000000" {
|
|
||||||
pos 1920,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
sane.user.fs.".config/waybar/config" = sane-lib.fs.wantedSymlinkTo waybar-config-text;
|
|
||||||
|
|
||||||
# style docs: https://github.com/Alexays/Waybar/wiki/Styling
|
|
||||||
sane.user.fs.".config/waybar/style.css" = sane-lib.fs.wantedText ''
|
|
||||||
* {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* defaults below: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
|
||||||
window#waybar {
|
|
||||||
background-color: rgba(43, 48, 59, 0.5);
|
|
||||||
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
|
|
||||||
color: #ffffff;
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.hidden {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
window#waybar.empty {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
window#waybar.solo {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
window#waybar.termite {
|
|
||||||
background-color: #3F3F3F;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.chromium {
|
|
||||||
background-color: #000000;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button {
|
|
||||||
padding: 0 5px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
/* Use box-shadow instead of border so the text isn't offset */
|
|
||||||
box-shadow: inset 0 -3px transparent;
|
|
||||||
/* Avoid rounded borders under each workspace name */
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
|
||||||
#workspaces button:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.focused {
|
|
||||||
background-color: #64727D;
|
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.urgent {
|
|
||||||
background-color: #eb4d4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mode {
|
|
||||||
background-color: #64727D;
|
|
||||||
border-bottom: 3px solid #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clock,
|
|
||||||
#battery,
|
|
||||||
#cpu,
|
|
||||||
#memory,
|
|
||||||
#disk,
|
|
||||||
#temperature,
|
|
||||||
#backlight,
|
|
||||||
#network,
|
|
||||||
#pulseaudio,
|
|
||||||
#custom-media,
|
|
||||||
#tray,
|
|
||||||
#mode,
|
|
||||||
#idle_inhibitor,
|
|
||||||
#mpd {
|
|
||||||
padding: 0 10px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window,
|
|
||||||
#workspaces {
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If workspaces is the leftmost module, omit left margin */
|
|
||||||
.modules-left > widget:first-child > #workspaces {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If workspaces is the rightmost module, omit right margin */
|
|
||||||
.modules-right > widget:last-child > #workspaces {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clock {
|
|
||||||
background-color: #64727D;
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery.charging, #battery.plugged {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #26A65B;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink {
|
|
||||||
to {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery.critical:not(.charging) {
|
|
||||||
background-color: #f53c3c;
|
|
||||||
color: #ffffff;
|
|
||||||
animation-name: blink;
|
|
||||||
animation-duration: 0.5s;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
|
|
||||||
label:focus {
|
|
||||||
background-color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cpu {
|
|
||||||
background-color: #2ecc71;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#memory {
|
|
||||||
background-color: #9b59b6;
|
|
||||||
}
|
|
||||||
|
|
||||||
#disk {
|
|
||||||
background-color: #964B00;
|
|
||||||
}
|
|
||||||
|
|
||||||
#backlight {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#network {
|
|
||||||
background-color: #2980b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
#network.disconnected {
|
|
||||||
background-color: #f53c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pulseaudio {
|
|
||||||
background-color: #f1c40f;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pulseaudio.muted {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
color: #2a5c45;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media {
|
|
||||||
background-color: #66cc99;
|
|
||||||
color: #2a5c45;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media.custom-spotify {
|
|
||||||
background-color: #66cc99;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media.custom-vlc {
|
|
||||||
background-color: #ffa000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature {
|
|
||||||
background-color: #f0932b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature.critical {
|
|
||||||
background-color: #eb4d4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray {
|
|
||||||
background-color: #2980b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > .passive {
|
|
||||||
-gtk-icon-effect: dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > .needs-attention {
|
|
||||||
-gtk-icon-effect: highlight;
|
|
||||||
background-color: #eb4d4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#idle_inhibitor {
|
|
||||||
background-color: #2d3436;
|
|
||||||
}
|
|
||||||
|
|
||||||
#idle_inhibitor.activated {
|
|
||||||
background-color: #ecf0f1;
|
|
||||||
color: #2d3436;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd {
|
|
||||||
background-color: #66cc99;
|
|
||||||
color: #2a5c45;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.disconnected {
|
|
||||||
background-color: #f53c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.stopped {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.paused {
|
|
||||||
background-color: #51a37a;
|
|
||||||
}
|
|
||||||
|
|
||||||
#language {
|
|
||||||
background: #00b093;
|
|
||||||
color: #740864;
|
|
||||||
padding: 0 5px;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state {
|
|
||||||
background: #97e1ad;
|
|
||||||
color: #000000;
|
|
||||||
padding: 0 0px;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label {
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label.locked {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
# style = ''
|
|
||||||
# * {
|
|
||||||
# border: none;
|
|
||||||
# border-radius: 0;
|
|
||||||
# font-family: Source Code Pro;
|
|
||||||
# }
|
|
||||||
# window#waybar {
|
|
||||||
# background: #16191C;
|
|
||||||
# color: #AAB2BF;
|
|
||||||
# }
|
|
||||||
# #workspaces button {
|
|
||||||
# padding: 0 5px;
|
|
||||||
# }
|
|
||||||
# .custom-spotify {
|
|
||||||
# padding: 0 10px;
|
|
||||||
# margin: 0 4px;
|
|
||||||
# background-color: #1DB954;
|
|
||||||
# color: black;
|
|
||||||
# }
|
|
||||||
# '';
|
|
||||||
|
|
||||||
sane.packages.extraUserPkgs = with pkgs; [
|
|
||||||
swaylock
|
|
||||||
swayidle # (unused)
|
|
||||||
wl-clipboard
|
|
||||||
mako # notification daemon
|
|
||||||
xdg-utils # for xdg-open
|
|
||||||
# user stuff
|
|
||||||
# pavucontrol
|
|
||||||
sway-contrib.grimshot
|
|
||||||
gnome.gnome-bluetooth
|
|
||||||
gnome.gnome-control-center
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@@ -1,21 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
# give each host a shortname that all the other hosts know, to allow easy comms.
|
|
||||||
networking.hosts = lib.mkMerge [
|
|
||||||
(lib.mapAttrs' (host: cfg: {
|
|
||||||
# bare-name for LAN addresses
|
|
||||||
# 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
|
|
||||||
name = cfg.lan-ip;
|
|
||||||
value = [ host ];
|
|
||||||
}) config.sane.hosts.by-name)
|
|
||||||
(lib.mapAttrs' (host: cfg: {
|
|
||||||
# -hn suffixed name for communication over my wg-home VPN.
|
|
||||||
# hn = "home network"
|
|
||||||
name = cfg.wg-home.ip;
|
|
||||||
value = [ "${host}-hn" ];
|
|
||||||
}) config.sane.hosts.by-name)
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,100 +0,0 @@
|
|||||||
{ 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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,18 +0,0 @@
|
|||||||
{ 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" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
{ 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;
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
{ 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" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./client
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,6 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./duplicity.nix
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,80 +0,0 @@
|
|||||||
{ 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,32 +1,29 @@
|
|||||||
{ config, pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./net.nix
|
./net.nix
|
||||||
./secrets.nix
|
./users.nix
|
||||||
./services
|
./services
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.packages.extraUserPkgs = with pkgs; [
|
sane.packages.extraUserPkgs = [
|
||||||
# for administering services
|
# for administering services
|
||||||
freshrss
|
pkgs.matrix-synapse
|
||||||
matrix-synapse
|
pkgs.freshrss
|
||||||
signaldctl
|
|
||||||
];
|
];
|
||||||
sane.persist.enable = true;
|
sane.persist.enable = true;
|
||||||
sane.services.dyn-dns.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
|
# 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;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
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.
|
# both transmission and ipfs try to set different net defaults.
|
||||||
# we just use the most aggressive of the two here:
|
# we just use the most aggressive of the two here:
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
@@ -52,18 +52,18 @@
|
|||||||
|
|
||||||
# services.resolved.extraConfig = ''
|
# services.resolved.extraConfig = ''
|
||||||
# # docs: `man resolved.conf`
|
# # docs: `man resolved.conf`
|
||||||
# # DNS servers to use via the `wg-ovpns` interface.
|
# # DNS servers to use via the `wg0` interface.
|
||||||
# # i hope that from the root ns, these aren't visible.
|
# # i hope that from the root ns, these aren't visible.
|
||||||
# DNS=46.227.67.134%wg-ovpns 192.165.9.158%wg-ovpns
|
# DNS=46.227.67.134%wg0 192.165.9.158%wg0
|
||||||
# FallbackDNS=1.1.1.1 9.9.9.9
|
# FallbackDNS=1.1.1.1 9.9.9.9
|
||||||
# '';
|
# '';
|
||||||
|
|
||||||
# OVPN CONFIG (https://www.ovpn.com):
|
# OVPN CONFIG (https://www.ovpn.com):
|
||||||
# DOCS: https://nixos.wiki/wiki/WireGuard
|
# DOCS: https://nixos.wiki/wiki/WireGuard
|
||||||
# if you `systemctl restart wireguard-wg-ovpns`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
# if you `systemctl restart wireguard-wg0`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
|
||||||
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
# TODO: why not create the namespace as a seperate operation (nix config for that?)
|
||||||
networking.wireguard.enable = true;
|
networking.wireguard.enable = true;
|
||||||
networking.wireguard.interfaces.wg-ovpns = let
|
networking.wireguard.interfaces.wg0 = let
|
||||||
ip = "${pkgs.iproute2}/bin/ip";
|
ip = "${pkgs.iproute2}/bin/ip";
|
||||||
in-ns = "${ip} netns exec ovpns";
|
in-ns = "${ip} netns exec ovpns";
|
||||||
iptables = "${pkgs.iptables}/bin/iptables";
|
iptables = "${pkgs.iptables}/bin/iptables";
|
||||||
@@ -163,6 +163,9 @@
|
|||||||
'';
|
'';
|
||||||
networking.iproute2.enable = true;
|
networking.iproute2.enable = true;
|
||||||
|
|
||||||
|
sops.secrets."wg_ovpns_privkey" = {
|
||||||
|
sopsFile = ../../secrets/servo.yaml;
|
||||||
|
};
|
||||||
|
|
||||||
# HURRICANE ELECTRIC CONFIG:
|
# HURRICANE ELECTRIC CONFIG:
|
||||||
# networking.sits = {
|
# networking.sits = {
|
@@ -24,4 +24,8 @@ lib.mkIf false
|
|||||||
OnUnitActiveSec = "10min";
|
OnUnitActiveSec = "10min";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sops.secrets."ddns_afraid" = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
|
};
|
||||||
}
|
}
|
@@ -27,4 +27,8 @@ lib.mkIf false
|
|||||||
OnUnitActiveSec = "10min";
|
OnUnitActiveSec = "10min";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sops.secrets."ddns_he" = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
|
};
|
||||||
}
|
}
|
@@ -46,8 +46,6 @@
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
# provide access to certs
|
# 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" ];
|
users.users.ejabberd.extraGroups = [ "nginx" ];
|
||||||
|
|
||||||
security.acme.certs."uninsane.org".extraDomainNames = [
|
security.acme.certs."uninsane.org".extraDomainNames = [
|
@@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, ... }:
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
{
|
{
|
||||||
sops.secrets."freshrss_passwd" = {
|
sops.secrets.freshrss_passwd = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.freshrss.name;
|
owner = config.users.users.freshrss.name;
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
};
|
};
|
||||||
@@ -41,10 +42,7 @@
|
|||||||
description = "import sane RSS feed list";
|
description = "import sane RSS feed list";
|
||||||
after = [ "freshrss-config.service" ];
|
after = [ "freshrss-config.service" ];
|
||||||
script = ''
|
script = ''
|
||||||
# easiest way to preserve feeds: delete the user, recreate it, import feeds
|
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
|
||||||
${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,17 +15,6 @@
|
|||||||
services.gitea.settings.session.COOKIE_SECURE = true;
|
services.gitea.settings.session.COOKIE_SECURE = true;
|
||||||
# services.gitea.disableRegistration = 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 = {
|
services.gitea.settings = {
|
||||||
server = {
|
server = {
|
||||||
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
|
# options: "home", "explore", "organizations", "login" or URL fragment (or full URL)
|
@@ -7,8 +7,8 @@
|
|||||||
];
|
];
|
||||||
services.jackett.enable = true;
|
services.jackett.enable = true;
|
||||||
|
|
||||||
systemd.services.jackett.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.jackett.after = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.jackett.partOf = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.jackett.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.jackett.serviceConfig = {
|
systemd.services.jackett.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
@@ -6,18 +6,12 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./discord-puppet.nix
|
./discord-puppet.nix
|
||||||
# ./irc.nix
|
# ./irc.nix
|
||||||
./signal.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# allow synapse to read the registration files of its appservices
|
|
||||||
users.users.matrix-synapse.extraGroups = [ "mautrix-signal" ];
|
|
||||||
|
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.plaintext = [
|
||||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
|
||||||
];
|
];
|
||||||
services.matrix-synapse.enable = true;
|
services.matrix-synapse.enable = true;
|
||||||
# this changes the default log level from INFO to WARN.
|
|
||||||
# maybe there's an easier way?
|
|
||||||
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
|
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
|
||||||
services.matrix-synapse.settings.server_name = "uninsane.org";
|
services.matrix-synapse.settings.server_name = "uninsane.org";
|
||||||
|
|
||||||
@@ -133,7 +127,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
sops.secrets."matrix_synapse_secrets" = {
|
sops.secrets.matrix_synapse_secrets = {
|
||||||
|
sopsFile = ../../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.matrix-synapse.name;
|
owner = config.users.users.matrix-synapse.name;
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -43,7 +43,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: should use a dedicated user
|
|
||||||
systemd.services.mx-puppet-discord.serviceConfig = {
|
systemd.services.mx-puppet-discord.serviceConfig = {
|
||||||
# fix up to not use /var/lib/private, but just /var/lib
|
# fix up to not use /var/lib/private, but just /var/lib
|
||||||
DynamicUser = lib.mkForce false;
|
DynamicUser = lib.mkForce false;
|
@@ -1,8 +1,11 @@
|
|||||||
{ lib, ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.plaintext = [
|
||||||
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/navidrome"; }
|
# TODO: we don't have a static user allocated for navidrome!
|
||||||
|
# the chown would happen too early for us to set static perms
|
||||||
|
"/var/lib/private/navidrome"
|
||||||
|
# { user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
|
||||||
];
|
];
|
||||||
services.navidrome.enable = true;
|
services.navidrome.enable = true;
|
||||||
services.navidrome.settings = {
|
services.navidrome.settings = {
|
||||||
@@ -15,20 +18,6 @@
|
|||||||
ScanSchedule = "@every 1h";
|
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" = {
|
services.nginx.virtualHosts."music.uninsane.org" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
@@ -17,5 +17,5 @@
|
|||||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||||
|
|
||||||
sane.services.nixserve.enable = true;
|
sane.services.nixserve.enable = true;
|
||||||
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
|
sane.services.nixserve.sopsFile = ../../../secrets/servo.yaml;
|
||||||
}
|
}
|
@@ -135,11 +135,6 @@
|
|||||||
# CapabilityBoundingSet = lib.mkForce "~";
|
# 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
|
# Pleroma server and web interface
|
||||||
# TODO: enable publog?
|
# TODO: enable publog?
|
||||||
services.nginx.virtualHosts."fed.uninsane.org" = {
|
services.nginx.virtualHosts."fed.uninsane.org" = {
|
||||||
@@ -184,7 +179,8 @@
|
|||||||
|
|
||||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||||
|
|
||||||
sops.secrets."pleroma_secrets" = {
|
sops.secrets.pleroma_secrets = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.pleroma.name;
|
owner = config.users.users.pleroma.name;
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -1,6 +1,3 @@
|
|||||||
# DOCS:
|
|
||||||
# - dovecot config: <https://doc.dovecot.org/configuration_manual/>
|
|
||||||
|
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
@@ -113,8 +110,8 @@ in
|
|||||||
services.postfix.enableSubmissions = true;
|
services.postfix.enableSubmissions = true;
|
||||||
services.postfix.submissionsOptions = submissionOptions;
|
services.postfix.submissionsOptions = submissionOptions;
|
||||||
|
|
||||||
systemd.services.postfix.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.postfix.after = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.postfix.partOf = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.postfix.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.postfix.serviceConfig = {
|
systemd.services.postfix.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
@@ -135,8 +132,8 @@ in
|
|||||||
# keeping this the same as the hostname seems simplest
|
# keeping this the same as the hostname seems simplest
|
||||||
services.opendkim.selector = "mx";
|
services.opendkim.selector = "mx";
|
||||||
|
|
||||||
systemd.services.opendkim.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.opendkim.after = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.opendkim.partOf = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.opendkim.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.opendkim.serviceConfig = {
|
systemd.services.opendkim.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
@@ -146,25 +143,6 @@ in
|
|||||||
|
|
||||||
# inspired by https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/
|
# inspired by https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/
|
||||||
services.dovecot2.enable = true;
|
services.dovecot2.enable = true;
|
||||||
services.dovecot2.mailboxes = {
|
|
||||||
# special-purpose mailboxes: "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash"
|
|
||||||
# RFC6154 describes these special mailboxes: https://www.ietf.org/rfc/rfc6154.html
|
|
||||||
# how these boxes are treated is 100% up to the client and server to decide.
|
|
||||||
# client behavior:
|
|
||||||
# iOS
|
|
||||||
# - Drafts: ?
|
|
||||||
# - Sent: works
|
|
||||||
# - Trash: works
|
|
||||||
# aerc
|
|
||||||
# - Drafts: works
|
|
||||||
# - Sent: works
|
|
||||||
# - Trash: no; deleted messages are actually deleted
|
|
||||||
# use `:move trash` instead
|
|
||||||
# Sent mailbox: all sent messages are copied to it. unclear if this happens server-side or client-side.
|
|
||||||
Drafts = { specialUse = "Drafts"; auto = "create"; };
|
|
||||||
Sent = { specialUse = "Sent"; auto = "create"; };
|
|
||||||
Trash = { specialUse = "Trash"; auto = "create"; };
|
|
||||||
};
|
|
||||||
services.dovecot2.sslServerCert = "/var/lib/acme/imap.uninsane.org/fullchain.pem";
|
services.dovecot2.sslServerCert = "/var/lib/acme/imap.uninsane.org/fullchain.pem";
|
||||||
services.dovecot2.sslServerKey = "/var/lib/acme/imap.uninsane.org/key.pem";
|
services.dovecot2.sslServerKey = "/var/lib/acme/imap.uninsane.org/key.pem";
|
||||||
services.dovecot2.enablePAM = false;
|
services.dovecot2.enablePAM = false;
|
||||||
@@ -219,7 +197,8 @@ in
|
|||||||
# }
|
# }
|
||||||
];
|
];
|
||||||
|
|
||||||
sops.secrets."dovecot_passwd" = {
|
sops.secrets.dovecot_passwd = {
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
owner = config.users.users.dovecot2.name;
|
owner = config.users.users.dovecot2.name;
|
||||||
# TODO: debug why mail can't be sent without this being world-readable
|
# TODO: debug why mail can't be sent without this being world-readable
|
||||||
mode = "0444";
|
mode = "0444";
|
@@ -40,8 +40,8 @@
|
|||||||
# transmission will by default not allow the world to read its files.
|
# transmission will by default not allow the world to read its files.
|
||||||
services.transmission.downloadDirPermissions = "775";
|
services.transmission.downloadDirPermissions = "775";
|
||||||
|
|
||||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.after = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.partOf = [ "wireguard-wg0.service" ];
|
||||||
systemd.services.transmission.serviceConfig = {
|
systemd.services.transmission.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
@@ -9,7 +9,6 @@
|
|||||||
"192.168.0.5"
|
"192.168.0.5"
|
||||||
"10.0.1.5"
|
"10.0.1.5"
|
||||||
];
|
];
|
||||||
sane.services.trust-dns.quiet = true;
|
|
||||||
|
|
||||||
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
|
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
|
||||||
|
|
@@ -8,6 +8,7 @@ lib.mkIf false
|
|||||||
{
|
{
|
||||||
sops.secrets."mediawiki_pw" = {
|
sops.secrets."mediawiki_pw" = {
|
||||||
owner = config.users.users.mediawiki.name;
|
owner = config.users.users.mediawiki.name;
|
||||||
|
sopsFile = ../../../secrets/servo.yaml;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.mediawiki.enable = true;
|
services.mediawiki.enable = true;
|
24
hosts/servo/users.nix
Normal file
24
hosts/servo/users.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{ 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" ];
|
||||||
|
}
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 208377,
|
|
||||||
"content_type": "application/xml; charset=utf-8",
|
|
||||||
"description": "applieddivinitystudies@gmail.com",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 20,
|
|
||||||
"last_updated": "2022-12-20T01:23:44.546000+00:00",
|
|
||||||
"score": 26,
|
|
||||||
"self_url": "https://applieddivinitystudies.com/atom.xml",
|
|
||||||
"site_name": "Applied Divinity Studies",
|
|
||||||
"site_url": "https://applieddivinitystudies.com",
|
|
||||||
"title": "Applied Divinity Studies",
|
|
||||||
"url": "https://applieddivinitystudies.com/atom.xml",
|
|
||||||
"velocity": 0.079,
|
|
||||||
"version": "atom10"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 19245,
|
|
||||||
"content_type": "text/xml; charset=utf-8",
|
|
||||||
"description": "Austin Vernon's Blog",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 42,
|
|
||||||
"last_updated": "2023-01-05T00:00:00+00:00",
|
|
||||||
"score": 24,
|
|
||||||
"self_url": "",
|
|
||||||
"site_name": "Austin Vernon - Austin Vernon's Blog",
|
|
||||||
"site_url": "https://austinvernon.site",
|
|
||||||
"title": "Austin Vernon",
|
|
||||||
"url": "https://austinvernon.site/rss.xml",
|
|
||||||
"velocity": 0.063,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 453506,
|
|
||||||
"content_type": "text/xml; charset=utf-8",
|
|
||||||
"description": "Balaji Srinivasan's personal blog. Formerly CTO of Coinbase and General Partner at a16z, @balajis is an investor and founder.",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 15,
|
|
||||||
"last_updated": "2022-04-28T18:22:11+00:00",
|
|
||||||
"score": 16,
|
|
||||||
"self_url": "https://balajis.com/rss/",
|
|
||||||
"site_name": "Balaji Srinivasan",
|
|
||||||
"site_url": "https://balajis.com",
|
|
||||||
"title": "Balaji Srinivasan",
|
|
||||||
"url": "https://balajis.com/rss/",
|
|
||||||
"velocity": 0.01,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 213052,
|
|
||||||
"content_type": "application/rss+xml; charset=utf-8",
|
|
||||||
"description": "",
|
|
||||||
"favicon": "https://images.squarespace-cdn.com/content/v1/50363cf324ac8e905e7df861/ebdb4645-db93-4967-881d-db698ee59c2c/favicon.ico?format=100w",
|
|
||||||
"favicon_data_uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAB4FBMVEUAAADmRjIBAADmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjLmRjL///8mIgRMAAAAnnRSTlMAAAA6W1pZTTESpvnryogzAeaCFKwboQv38vP6X449P0FPe8b8vw5mhe02aAif/VhCZx/h4PVD1xwEmQVt3i1uBwwWPpv0Ybu8vtLx7m/JSf5IniT28DWLOTxAUX3EzSgXaXJxc0URa+elCjSdnKLWGewykUv4VSB87zgmHs8iKbZlCbFXhLfHGOpKY9VTjaOuUDAG2tvLGidGL+iWJUyE2tMAAAJwSURBVHja7df5U1JRGMZxnxItw6Q0FdTCoowyWiSJMLXQrMAyU9ojWxQFc8+MijYL23db+VsbTsCZC9xkbu9JnTnfH58Z+MywvAN5MplMJpPJFjuoRoqszNelVVC4ajUpAxTFMlqjL15bYgAEIbx160sBUQivbAMgCuGVVwCkSKUxmakq9dZUEyM1GzclMtdu3mKJsbZuo0XqAF7p9ko2WnfQIjvBJ6B+FxttuwUgfNxjYuteeoSHfQ1srRaL2OPj/kaBCGA2xkfHAXEI4DzIxhoDNQKeq4m974eaQYu0tFYkOnzE3dYen0xHqW+Xo+NYouMnPGzxdjqFH8iTpwzCT31Xx+lWiD/1pu4zAEiRnt5UqVtf7Esh+Fs5I2fPnU924eKly3+cK/7kM+Bq37XrWbtxE9pOPfoHAmwdHEoiQW9MpRA0fuMBnYcdr0IggVjVkGFNCFtdt9hc7hSA8LnAEp9HRjlC9XLxYGbna2xcgdh6rel5JjQjwORUfDa6FcjY7fFgWr7pf0DusHnmrgIJhJGZVgS4d5/N9gdKJAK6Xyvo746xHg5RIo8A3mP3ExtbbX2gREJPdYny62afJT+vz6NkiGpzL5ghFLG+NAhHXr1mhkik4U0YzFAgfmSWC/K2q0rZVLve8a4lYmCPVyI9ZW3DikIT0zkhJe9rlU02fhj9CE5wJEseXy5ItriwIGINIk97SwmZ8ZEjFvsnZZ+/NJMjc+7ovLKoixwJfEVGlIj6N14iEllGSBhZIkY8swNN6XV+o0TUKlrxH5DBZYZ4xb9c+D7yI5A9/U8ypP6XX6XIPAWw8P94mUwmk8lki95vmJ/g9Xmlv3QAAAAASUVORK5CYII=",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 20,
|
|
||||||
"last_updated": "2022-12-14T09:43:49+00:00",
|
|
||||||
"score": 8,
|
|
||||||
"self_url": "",
|
|
||||||
"site_name": "Benedict Evans",
|
|
||||||
"site_url": "https://www.ben-evans.com",
|
|
||||||
"title": "Essays - Benedict Evans",
|
|
||||||
"url": "https://www.ben-evans.com/benedictevans?format=rss",
|
|
||||||
"velocity": 0.033,
|
|
||||||
"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"
|
||||||
|
}
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 339384,
|
|
||||||
"content_type": "application/xml; charset=utf-8",
|
|
||||||
"description": "Yet another programming blog. Thoughts on software and related misadventures.",
|
|
||||||
"favicon": "https://bitbashing.io/favicon.ico",
|
|
||||||
"favicon_data_uri": "data:image/png;base64,AAABAAEAEBACAAEAAQCwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFBQAAAAAAP//AAD//wAA7/8AAO//AADv/wAA7/8AAO//AADvAwAA7/8AAO8DAADv/wAA7/8AAO//AADv/wAA//8AAP//AAD//wAA//8AAO//AADv/wAA7/8AAO//AADv/wAA7wMAAO//AADvAwAA7/8AAO//AADv/wAA7/8AAP//AAD//wAA",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 10,
|
|
||||||
"last_updated": "2022-11-22T00:00:00+00:00",
|
|
||||||
"score": 20,
|
|
||||||
"self_url": "https://bitbashing.io/feed.xml",
|
|
||||||
"site_name": "Bit Bashing",
|
|
||||||
"site_url": "https://bitbashing.io",
|
|
||||||
"title": "Bit Bashing",
|
|
||||||
"url": "https://bitbashing.io/feed.xml",
|
|
||||||
"velocity": 0.003,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 1,
|
|
||||||
"content_length": 343256,
|
|
||||||
"content_type": "text/xml; charset=utf-8",
|
|
||||||
"description": null,
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 60,
|
|
||||||
"last_updated": "2022-11-07T00:00:00+00:00",
|
|
||||||
"score": -4,
|
|
||||||
"self_url": "",
|
|
||||||
"site_name": "Daniel Janus \u2013 blog",
|
|
||||||
"site_url": "https://blog.danieljanus.pl",
|
|
||||||
"title": "code \u00b7 words \u00b7 emotions: Daniel Janus\u2019s blog",
|
|
||||||
"url": "https://blog.danieljanus.pl/atom.xml",
|
|
||||||
"velocity": 0.011,
|
|
||||||
"version": "atom10"
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 623592,
|
|
||||||
"content_type": "application/atom+xml; charset=utf-8",
|
|
||||||
"description": "I'm David Rosenthal, and this is a place to discuss the work I'm doing in Digital Preservation.",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [
|
|
||||||
"http://pubsubhubbub.appspot.com/"
|
|
||||||
],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": true,
|
|
||||||
"item_count": 25,
|
|
||||||
"last_updated": "2023-01-10T17:59:42.157000+00:00",
|
|
||||||
"score": 20,
|
|
||||||
"self_url": "https://www.blogger.com/feeds/4503292949532760618/posts/default",
|
|
||||||
"site_name": "DSHR's Blog",
|
|
||||||
"site_url": "https://blog.dshr.org",
|
|
||||||
"title": "DSHR's Blog",
|
|
||||||
"url": "https://blog.dshr.org/feeds/posts/default",
|
|
||||||
"velocity": 0.35,
|
|
||||||
"version": "atom10"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 45559,
|
|
||||||
"content_type": "application/rss+xml; charset=utf-8",
|
|
||||||
"description": "bunnie's blog",
|
|
||||||
"favicon": "https://www.bunniestudios.com/favicon.ico",
|
|
||||||
"favicon_data_uri": "data:image/png;base64,AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAFBQUADU1NQBdXV0AoKCgACYmJgBpaWkATk5OAIODgwAKCgoAfHx8ALi4uACxsbEAAgICAKOjowA+Pj4AAQEBAERERADy8vIABwcHABsbGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAASAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAALAAAAAAAAAAAAABMAAAAAAAAAAAAAAAAABQAACgAAAAAPAAAAAAAAAAkAAAAAAAAOAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCQAAAAAAAAAAAAAAAAAAABANAAAAAAAAAAAAAAAAAP//AADAfQAAwTgAAMeZAADPnwAAz88AAM/PAADPzwAAz58AAMefAADDHwAAyD8AAM//AADP/wAAD/8AAA//AAA=",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 5,
|
|
||||||
"last_updated": "2023-01-04T15:49:57+00:00",
|
|
||||||
"score": 20,
|
|
||||||
"self_url": "https://www.bunniestudios.com/blog/?feed=rss2",
|
|
||||||
"site_name": " bunnie's blog",
|
|
||||||
"site_url": "https://www.bunniestudios.com",
|
|
||||||
"title": "bunnie's blog",
|
|
||||||
"url": "https://www.bunniestudios.com/blog/?feed=rss2",
|
|
||||||
"velocity": 0.114,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 15076852,
|
|
||||||
"content_type": "application/rss+xml; charset=utf-8",
|
|
||||||
"description": "Congressional Dish is a twice-monthly podcast that aims to draw attention to where the American people truly have power: Congress. From the perspective of a fed up taxpayer with no allegiance to any political party, Jennifer Briney will fill you in on the must-know information about what our representatives do AFTER the elections and how their actions can and will affect our day to day lives. \nHosted by @JenBriney. \n\nLinks to information sources available at www.congressionaldish.com",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": true,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 269,
|
|
||||||
"last_updated": "2023-01-06T18:13:57+00:00",
|
|
||||||
"score": 0,
|
|
||||||
"self_url": "https://feeds.libsyn.com/39908/rss",
|
|
||||||
"site_name": "",
|
|
||||||
"site_url": "",
|
|
||||||
"title": "Congressional Dish",
|
|
||||||
"url": "https://feeds.libsyn.com/39908/rss",
|
|
||||||
"velocity": 0.071,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 140827,
|
|
||||||
"content_type": "application/rss+xml; charset=utf-8",
|
|
||||||
"description": "Latest gwern.net updates, interesting links, and reviews",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 14,
|
|
||||||
"last_updated": "2021-06-11T14:16:22+00:00",
|
|
||||||
"score": -6,
|
|
||||||
"self_url": "https://gwern.substack.com/feed",
|
|
||||||
"site_name": "",
|
|
||||||
"site_url": "",
|
|
||||||
"title": "Gwern.net Newsletter",
|
|
||||||
"url": "https://gwern.substack.com/feed",
|
|
||||||
"velocity": 0.032,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 359714,
|
|
||||||
"content_type": "text/xml; charset=utf-8",
|
|
||||||
"description": "Ian Henry's blog.",
|
|
||||||
"favicon": "https://ianthehenry.com/favicon.ico",
|
|
||||||
"favicon_data_uri": "data:image/png;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAHH1uIDShg/xEsZv8GFk3/CSJb/wgZT/8IGU//CBlP/wkiW/8HGE7/CBlP/wkiW/8HIFX/BxhO/wcgVf8OK2t7JDhz/wcfW/8NNn7/CSpq/wkjYf8MJGP/DCJg/w02ef9SqdX/TKHJ/1my2f9Moc3/UqzV/1Cr1v9Pocf/Ditr/xk2cf8KJGL/CjR8/wwva/8RK2r/Cido/w8qa/8LLW//TqjT/1Gnz/9bsNf/Sp/K/1Wt2f9Wrtj/UKjQ/w8xc/8gPHj/Di5w/xk4ef8TL27/Dy5u/xU3fv8QLGv/Ditr/1qy3f9dttv/Yrje/0ecx/9Knsz/U6fQ/1m13f8YN3n/JjZx/wszdv8YOXz/Eilm/xc3e/8dSZT/GDJz/xk/hv9hu+P/WbHZ/1mv1v9Kn8z/WLHd/1212v9ds9n/Eipr/xcqYv8SOX3/GzRz/xQrZ/8aM3D/HEuU/xc0df8fPYD/Yrre/1603P9dt9z/VKvY/1iz4v9etNr/Y73m/xk5fP8cMm3/JEWK/xc0dP8eO3v/Hjd4/ydRnP8WM3P/Gjd3/2bA5/9dtd//Ybng/2K95v9ct+P/Ybnf/2K95/8oS5L/GChg/xxBh/8lSIz/GC5s/yM5ev8hRoz/HTp7/yE6e/9Zsdz/YLzn/2jF7v9ryfL/YLzn/1qu1f9htdz/GDyD/y0/ev9qx/X/acDn/1235v9euun/Yb3r/1665v9owu7/GUOL/yhGh/8nUJL/MkmI/yBFiP8jOHb/MVag/yM+gv8VMGv/acXx/27G7/9szv3/bMb1/2zD6/9vy/X/edf5/yBHjv8yS47/JEuO/zVLiv8nS5D/LkOB/yRXpP8lQoT/H0WI/3fS+P910Pf/b8/8/2bB7/991Pn/eNb+/3nT+P8pS5L/O1GS/ytUnP86UJH/NF2k/zNLjP8sXKv/JkCB/x4+ff+B2/3/etT9/4He//922f//fNf//3TT//991/7/NU6N/zZSkP86Zav/PlWR/zFepv88ZKb/MF2n/yQ/gf8bNXH/fd3//3/b//+K6P//ft///3vV//993f//gdz//0hWlf9FZKX/RmOj/0JXlv9IeMP/QXKz/0Jsuf8eO3z/Hjt2/3vd//+C3v//ju3//3zg//+H4v//guL//4nj//9HXp3/UGur/ztgqf9JYqP/S3G4/05ur/9EbLT/Hjt7/zNJhf+G4///heT//4rq//+E5///iOT//4jr//+G4///UXO0/09iov9EX6H/SWWn/1V5wP9Kaa7/RGSp/ydLj/8NKGCfDShg/x8+d/8RLGb/CBlP/wYWTf8GFEj/CBNH/wooZP8JIlv/CCJd/woWSf8HGE7/ByBV/wwgU/8fO3mfAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 10,
|
|
||||||
"last_updated": "2022-07-11T00:00:00+00:00",
|
|
||||||
"score": 20,
|
|
||||||
"self_url": "https://ianthehenry.com/feed.xml",
|
|
||||||
"site_name": "Ian Henry",
|
|
||||||
"site_url": "https://ianthehenry.com",
|
|
||||||
"title": "Ian Henry",
|
|
||||||
"url": "https://ianthehenry.com/feed.xml",
|
|
||||||
"velocity": 0.027,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 88585,
|
|
||||||
"content_type": "text/xml; charset=utf-8",
|
|
||||||
"description": "<p>The most unruly and least considered, most shameful among various Idiomdrottning components and libraries can be found here.</p>\n <p>To contact me, <a href=\"mailto:sandra.snan@idiomdrottning.org\">send mail to sandra.snan@idiomdrottning.org</a></p>",
|
|
||||||
"favicon": "https://idiomdrottning.org/favicon.png",
|
|
||||||
"favicon_data_uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kKHQ8EITJ/4sMAAAUJSURBVFjD7ZZ5bFRVGMV/983W6d5KoS21gFKWqpWEQkqCoCxqRHCBKEhcE8SYGsRgSEREoyaEJZgQETFBggpxRSBUlrBoF8rSlrZpQUqhsRVIO53SbabzZt69/jF06NBOW8ofxIT710vefe+ce853vvsJpZTiDi6NO7zuErhL4P9NoDPB0qsz0DTfFgEhBABV328OPIciGYrgbVvgaWqkeuc3PYIopRBCcHbL+pAEtb5llr2+d9dfobWmmst/7u8GIoSgsfQUjpLCgVvQ4aincvPakDIaHS7M9nDK1q5ESRl0er3lGrmL5xKRnDpwAvaERJqrKih492W8Lde6EfG5XSAEerMTj9OBUiogfeWXqwGIHD5y4ASUUmQs/YSGk3nsn5VJ2z8XEULcKC7DACGQus7V3IMIIRBC0FxVwYUdXwMQO/rBbkWptzSjlOpfEUakDCN2VDrS6+HIghlczT8S8NtksYJSIAQ1v+/wx9IwOP1hNuaIKITJRHji0ACwEIILO7ZQvmEVQohgAm21l0JGLeuL7wCBkgZ5b83l38P7/BJabf59ECg2R1EBrZeq/GpoJmxxgxBCoDc3kZ89n7J1Kxm3fHV3C1qr/2bXhGTy336RuoN78Lna/RIDtth4hj37EkopLJHRFC57nabKUky2sIACmtmCs7yIguwFoGkoKYkenoYlKhpnRQn7pqXTcCqf1FnzMIWF+Yl3DiSdhdNYdorcRc+jpIHJFkZ4cipDpz/N2MXLMLw6u7NS0SxWv/y2MB7dnsOB2ROxREQhvV6GTHqM+pN/ITQNX4ebR776GUdJIVXbN6GUQnq9zMmrxhRm9yvUdSLqJNF84SzHl75CR/1VhKYhvTrCbOGBd1ZgCY+gdM2KQI+IHZNBU+UZNJM56B8AZnsEKU8+R/WPWzFZrSilyPx0Iykzn7lhcaiRzPB0cHTh47TVXkJoN5wSZjNCCAxdD9ndekyTlETdN4rpOw8H11hfM+H5bRspW7cSc2R0UPx6Be/8Y5ct9iHJzPglF81s7p2A3tyEz9WOr8OF8hmYrDYaio9Tvv4jpM/bK7AKxgxYYrKFMXXbPqKGj+zern1ul7p8LIe6A3toPHMCZRgoKf13gOpyFCWD/L0VEjN/yyc8KSVEJ9Q0rDHxRKaOwBafgKfJgc/djjJ8fgJKwfULqRNchdBcoQLgSkqscfcw/adjIcEDFnQ9mbetlZrdP3D5cA7u+it421owXO2dXQmEQABKCIS6DirEdXARAI9Lf5gpW/f2WS89xjCQBN2D4Xbha2/l2rlynOXF1B3ajaexIajYpJBoaH4NpCRj2WeMmPdqv1LSZwo8Tgd1h/bgKCqgoagAX1urX4WAHQoUSN1DQuZknBXFzMmtDopuvwl0KqCUwtPYQPmGVdTm/IpmsyGE1uOopZkt2BOTmfD5JmJHP8TRhU9gT0oma923Pap68zLffPEooPC912g4nYfUdUxh9qAPpM+H1D2EJ6Uw6o0lJE6eQUTyvYH3LRfP0VJzHr25CUtUTJ9KBBVh7f5dFH28JHABKWmgDANLZBThyalEjUgjPiOTwROnEJM2tpsiTRUlWGPiKV3zAW01VYx5832GzX6hfxbkZ8/HUVyI0EyEJ6UQlz6OQeOzGDxpGvaEIYQq1J4GGMPdzt6paUzbeZTo+0f30TWVUobXqzqcDuV1u1RPS0qpbmX5PB3qj6fGqxPLF/W59z/I3vExRaQkUAAAAABJRU5ErkJggg==",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 20,
|
|
||||||
"last_updated": "2023-01-25T13:06:38+00:00",
|
|
||||||
"score": 10,
|
|
||||||
"self_url": "https://idiomdrottning.org/blog",
|
|
||||||
"site_name": "Idiomdrottning",
|
|
||||||
"site_url": "https://idiomdrottning.org",
|
|
||||||
"title": "Idiomdrottning",
|
|
||||||
"url": "https://idiomdrottning.org/blog",
|
|
||||||
"velocity": 0.441,
|
|
||||||
"version": "atom10"
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
{}
|
|
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"bozo": 0,
|
|
||||||
"content_length": 80033,
|
|
||||||
"content_type": "application/xml; charset=utf-8",
|
|
||||||
"description": "A blog by Matt Webb. My notebook and space for thinking out loud since February 2000.",
|
|
||||||
"favicon": "",
|
|
||||||
"favicon_data_uri": "",
|
|
||||||
"hubs": [],
|
|
||||||
"is_podcast": false,
|
|
||||||
"is_push": false,
|
|
||||||
"item_count": 8,
|
|
||||||
"last_updated": "2023-01-24T20:48:00+00:00",
|
|
||||||
"score": 22,
|
|
||||||
"self_url": "",
|
|
||||||
"site_name": "Matt Webb",
|
|
||||||
"site_url": "https://interconnected.org",
|
|
||||||
"title": "Interconnected",
|
|
||||||
"url": "https://interconnected.org/home/feed",
|
|
||||||
"velocity": 0.279,
|
|
||||||
"version": "rss20"
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user