This commit is contained in:
Shelvacu
2025-04-19 13:17:36 -07:00
committed by Shelvacu on fw
parent 086a258c92
commit 9bad53f188
28 changed files with 859 additions and 628 deletions

View File

@@ -257,7 +257,11 @@ else
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17";
};
"rsync.net" = {
extraHostNames = [ "rsn" "rsyncnet" "fm2382.rsync.net" ];
extraHostNames = [
"rsn"
"rsyncnet"
"fm2382.rsync.net"
];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdUkGe6kKn5ssz4WRZKjcws0InbQqZayenzk9obmP1z";
};

View File

@@ -87,7 +87,10 @@ lib.mkMerge [
})
{
vacu.packages.ffmpeg-vacu-full.enable = config.vacu.systemKind == "desktop";
vacu.packages.ffmpeg-vacu-headless.enable = config.vacu.systemKind != "minimal" && config.vacu.systemKind != "container" && config.vacu.systemKind != "desktop";
vacu.packages.ffmpeg-vacu-headless.enable =
config.vacu.systemKind != "minimal"
&& config.vacu.systemKind != "container"
&& config.vacu.systemKind != "desktop";
}
{
vacu.packages = {
@@ -122,63 +125,62 @@ lib.mkMerge [
};
}
{
vacu.packages =
with pkgs; [
bash
bzip2
curl
ddrescue
diffutils
dig
dnsutils
ethtool
file
findutils
gnugrep
gnused
gnutar
gnutls
gzip
hostname
htop
inetutils
iperf3
iputils
jq
killall
lsof
mosh
nano
ncdu
netcat-openbsd
nixos-rebuild
openssh
"p7zip-unfree"
pciutils
progress
psutils
pv
ripgrep
rsync
screen
# sed => gnused
sops
sshfs
ssh-to-age
# tar => gnutar
tmux
tree
tzdata
# units => vacu-units
unzip
usbutils
util-linux
"vacu-units"
vim
wget
which
xz
zip
];
vacu.packages = with pkgs; [
bash
bzip2
curl
ddrescue
diffutils
dig
dnsutils
ethtool
file
findutils
gnugrep
gnused
gnutar
gnutls
gzip
hostname
htop
inetutils
iperf3
iputils
jq
killall
lsof
mosh
nano
ncdu
netcat-openbsd
nixos-rebuild
openssh
"p7zip-unfree"
pciutils
progress
psutils
pv
ripgrep
rsync
screen
# sed => gnused
sops
sshfs
ssh-to-age
# tar => gnutar
tmux
tree
tzdata
# units => vacu-units
unzip
usbutils
util-linux
"vacu-units"
vim
wget
which
xz
zip
];
}
]

View File

@@ -33,7 +33,8 @@ let
readOnly = true;
};
};
config.finalPackage = if config.overrides == null then config.package else config.package.override config.overrides;
config.finalPackage =
if config.overrides == null then config.package else config.package.override config.overrides;
}
)
);

View File

@@ -47,7 +47,9 @@ in
};
config =
{
vacu.sourceTree = inputsOf inputs.self // { inherit inputs; };
vacu.sourceTree = inputsOf inputs.self // {
inherit inputs;
};
# vacu.sourceTree = pkgs.runCommand "inputs-tree" { } ''
# mkdir -p $out
# ln -s ${inputs.self} $out/self

View File

@@ -1,8 +1,4 @@
{
pkgs,
inputs,
...
}:
{ pkgs, inputs, ... }:
{
imports = [
inputs.jovian.nixosModules.jovian

View File

@@ -175,10 +175,11 @@
map (name: lib.nameValuePair name inputs.${name + suffix}) inp'
);
in
thisInputsA // {
inherit nixpkgs;
inherit (inputs) self;
};
thisInputsA
// {
inherit nixpkgs;
inherit (inputs) self;
};
mkNixosConfig =
{
unstable ? false,
@@ -210,7 +211,12 @@
);
lib = {
inherit mkPlain mkPkgs mkInputs mkNixosConfig;
inherit
mkPlain
mkPkgs
mkInputs
mkNixosConfig
;
};
nixosConfigurations = {
@@ -268,9 +274,7 @@
inherit (inputs) dns;
vacuModuleType = "nix-on-droid";
};
pkgs = mkPkgs {
system = arm;
};
pkgs = mkPkgs { system = arm; };
};
checks = nixpkgs.lib.genAttrs [ x86 ] (
@@ -286,15 +290,15 @@
node.specialArgs.selfPackages = self.packages.${system};
node.specialArgs.vacuModuleType = "nixos";
};
mkTest = name:
mkTest =
name:
nixpkgs.lib.nixos.runTest {
imports = [
commonTestModule
./tests/${name}
{ node.specialArgs.inputs = self.nixosConfigurations.${name}._module.specialArgs.inputs; }
];
}
;
};
checksFromConfig = plain.config.vacu.checks;
in
assert !(checksFromConfig ? liam) && !(checksFromConfig ? trip);
@@ -421,7 +425,8 @@
// (inputs.flake-utils.lib.eachDefaultSystem (
system:
let
mkNixvim = unstable:
mkNixvim =
unstable:
let
nixvim-input = if unstable then inputs.nixvim-unstable else inputs.nixvim;
in
@@ -437,12 +442,8 @@
config.allowUnfree = true;
overlays = [ inputs.sm64baserom.overlays.default ];
};
pkgs-unstable = mkPkgs (nixpkgs-args // {
useUnstable = true;
});
pkgs-stable = mkPkgs (nixpkgs-args // {
useUnstable = false;
});
pkgs-unstable = mkPkgs (nixpkgs-args // { useUnstable = true; });
pkgs-stable = mkPkgs (nixpkgs-args // { useUnstable = false; });
_plain = mkPlain pkgs-unstable;
plain = _plain.config.vacu.withAsserts _plain;
treefmtEval = inputs.treefmt-nix.lib.evalModule pkgs-unstable ./treefmt.nix;
@@ -473,14 +474,18 @@
};
generated = pkgs.linkFarm "generated" {
nixpkgs = "${inputs.nixpkgs}";
"liam-test/hints.py" = pkgs.writeText "hints.py" (import ./typesForTest.nix {
name = "liam";
inherit (pkgs-stable) lib;
inherit self;
inherit (inputs) nixpkgs;
});
"liam-test/hints.py" = pkgs.writeText "hints.py" (
import ./typesForTest.nix {
name = "liam";
inherit (pkgs-stable) lib;
inherit self;
inherit (inputs) nixpkgs;
}
);
"dns/python-env" = builtins.dirOf (builtins.dirOf dns.interpreter);
"mailtest/python-env" = builtins.dirOf (builtins.dirOf self.checks.x86_64-linux.liam.nodes.checker.vacu.mailtest.smtp.interpreter);
"mailtest/python-env" = builtins.dirOf (
builtins.dirOf self.checks.x86_64-linux.liam.nodes.checker.vacu.mailtest.smtp.interpreter
);
};
haproxy-auth-request = pkgs.callPackage ./packages/haproxy-auth-request.nix {
inherit haproxy-lua-http;

View File

@@ -1,8 +1,11 @@
{ config, ... }:
{
vacu.packages = [ "tpm-fido" ];
users.groups.uhid = {};
users.users.shelvacu.extraGroups = [ config.security.tpm2.tssGroup config.users.groups.uhid.name ];
users.groups.uhid = { };
users.users.shelvacu.extraGroups = [
config.security.tpm2.tssGroup
config.users.groups.uhid.name
];
security.tpm2.enable = true;
security.tpm2.applyUdevRules = true;
services.udev.extraRules = ''

View File

@@ -13,8 +13,9 @@
background: #eee;
padding: 0 10px;
}
h1,h2,h3 { line-height: 1.2; }
h1, h2, h3 {
line-height: 1.2;
}
form {
padding: 5px;
@@ -22,21 +23,34 @@
border-radius: 3px;
margin: 5px;
}
form label { display: block; }
form label {
display: block;
}
</style>
</head>
<body>
<h1>Jobs info</h1>
<p>Hi! I am currently looking to find a Software Engineering job. I am a professional developer specializing in Ruby on Rails and Rust, with over 10 years of experience.</p>
<p>
Hi! I am currently looking to find a Software Engineering job. I am a
professional developer specializing in Ruby on Rails and Rust, with over
10 years of experience.
</p>
<p>
What I want in a job:
<ul>
<li><b>Fully Remote</b> - that means 1 visit to an office per month or less. Strongly preferred; non-remote jobs will have to be very tempting in every other category.</li>
<li>
<b>Fully Remote</b> - that means 1 visit to an office per month or
less. Strongly preferred; non-remote jobs will have to be very
tempting in every other category.
</li>
<li>Prefer direct hire, but not required.</li>
<li>W2 employment preferred, but also open to C2C/1099.</li>
<li>While I could be productive in nearly any language, I would most like to work with Rust.</li>
<li>
While I could be productive in nearly any language, I would most like
to work with Rust.
</li>
</ul>
</p>
@@ -44,41 +58,78 @@
Things you probably want to know:
<ul>
<li>I am a US citizen (USC), for work authorization purposes I can work anywhere in the US.</li>
<li>
I am a US citizen (USC), for work authorization purposes I can work
anywhere in the US.
</li>
<li>Open to both contract and permanent positions.</li>
<li>I can start within a week.</li>
<li>I do not have a formal degree, but more than enough experience to make up for it.</li>
<li>
I do not have a formal degree, but more than enough experience to make
up for it.
</li>
</ul>
</p>
<h2>Resume</h2>
<p>
<a href="/shelvacu-resume.pdf">You can see my latest, up-to-date resume here</a>.
<a href="/shelvacu-resume.pdf"
>You can see my latest, up-to-date resume here</a>.
</p>
<h2>To send me job offers</h2>
<p>Unfortunately I get a lot of bad job offers if I just give out my email to anyone. As such, I will only accept job offers sent using this form.</b>
<p>
Unfortunately I get a lot of bad job offers if I just give out my email to
anyone. As such, I will only accept job offers sent using this form.
</p>
<p>Each time you want to send me an email about a new job offer, you must return to this page. The process may change from time to time. If you have multiple jobs you think I am a good fit for, please combine them in one email.</p>
<p>
Each time you want to send me an email about a new job offer, you must
return to this page. The process may change from time to time. If you have
multiple jobs you think I am a good fit for, please combine them in one
email.
</p>
<form method="get" action="/email">
<label>
<input type="checkbox" name="confirm_fully_remote_or_exceptional" value="yes" required>
<span>This is a fully remote job, or you will give an exceptional reason why the offer should be considered despite not being fully remote.</span>
<input
type="checkbox"
name="confirm_fully_remote_or_exceptional"
value="yes"
required
>
<span>This is a fully remote job, or you will give an exceptional reason
why the offer should be considered despite not being fully
remote.</span>
</label>
<label>
<input type="checkbox" name="confirm_i_read_the_above" value="yes" required>
<span>You have read the above and will not ask about work authorization status, earliest date to start, or formal education because you already have that information.</span>
<input
type="checkbox"
name="confirm_i_read_the_above"
value="yes"
required
>
<span>You have read the above and will not ask about work authorization
status, earliest date to start, or formal education because you
already have that information.</span>
</label>
<label>
<input type="checkbox" name="confirm_include_pay" value="yes" required>
<span>You will include an estimated pay range. No "depends on experience", my resume clearly shows how much experience I have.</span>
<span>You will include an estimated pay range. No "depends on
experience", my resume clearly shows how much experience I
have.</span>
</label>
<label>
<input type="checkbox" name="confirm_no_ask_resume" value="yes" required>
<span>You will not ask for an updated resume. The latest resume is always available on this page.</span>
<input
type="checkbox"
name="confirm_no_ask_resume"
value="yes"
required
>
<span>You will not ask for an updated resume. The latest resume is
always available on this page.</span>
</label>
<label>
<div>Your email:</div>
@@ -87,6 +138,10 @@
<button type="submit">Submit</button>
</form>
<p>I apologize for the hassle. I promise that if you follow everything here before sending a job offer to me, I <i>will</i> respond, and I will make every effort to respond within 1 week.</p>
<p>
I apologize for the hassle. I promise that if you follow everything here
before sending a job offer to me, I <i>will</i> respond, and I will make
every effort to respond within 1 week.
</p>
</body>
</html>

View File

@@ -57,16 +57,20 @@ in
repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; };
package = mkOption { default = pkgs.borgbackup; };
cmd = mkOption { default = lib.getExe cfg.package; };
paths = mkOption { default = [
"/var/lib/mail"
"/var/lib/dovecot"
"/var/log"
]; };
paths = mkOption {
default = [
"/var/lib/mail"
"/var/lib/dovecot"
"/var/log"
];
};
keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; };
};
config = {
vacu.assertions = lib.singleton {
assertion = (lib.versionAtLeast cfg.package.version "1.4.0") && !(lib.versionAtLeast cfg.package.version "1.5.0");
assertion =
(lib.versionAtLeast cfg.package.version "1.4.0")
&& !(lib.versionAtLeast cfg.package.version "1.5.0");
message = "Only for version 1.4.x";
fatal = true;
};
@@ -75,7 +79,7 @@ in
owner = cfg.user;
};
# systemd.tmpfiles.settings."10-auto-borg" = lib.genAttrs cfg.paths (_:
# systemd.tmpfiles.settings."10-auto-borg" = lib.genAttrs cfg.paths (_:
# {
# # A+ = append to ACLs recursively
# "A+" = {
@@ -88,7 +92,7 @@ in
group = cfg.user;
home = "/var/lib/auto-borg";
};
users.groups.${cfg.user} = {};
users.groups.${cfg.user} = { };
systemd.services.auto-borg-gen-key = {
script = ''
set -euo pipefail

View File

@@ -5,7 +5,17 @@
...
}:
let
inherit (builtins) isString isList length head all isInt isAttrs isFloat isBool;
inherit (builtins)
isString
isList
length
head
all
isInt
isAttrs
isFloat
isBool
;
inherit (lib)
concatStrings
concatStringsSep
@@ -16,21 +26,13 @@ let
elemAt
mapAttrsToList
;
mapConcat = f: xs:
concatStrings (map f xs)
;
mapConcatSep = sep: f: xs:
concatStringsSep sep (map f xs)
;
mapConcatLines = f: xs:
mapConcatSep "\n" f xs
;
isListWhere = xs: f:
(isList xs) && (all f xs)
;
stringOrList = val: (isString val) || (
(isListWhere val isString) && (length val) > 0
);
mapConcat = f: xs: concatStrings (map f xs);
mapConcatSep =
sep: f: xs:
concatStringsSep sep (map f xs);
mapConcatLines = f: xs: mapConcatSep "\n" f xs;
isListWhere = xs: f: (isList xs) && (all f xs);
stringOrList = val: (isString val) || ((isListWhere val isString) && (length val) > 0);
listify = val: if isList val then val else [ val ];
email_folders = [
"24nm-domain@shelvacu.com"
@@ -89,36 +91,47 @@ let
is_match = regex: s: (match regex s) != null;
is_not_match = regex: s: !(is_match regex s);
only_printable_ascii = s: is_match "[ -~\r\n]*" s;
has_vars = s: lib.hasInfix ("$"+"{") s;
has_vars = s: lib.hasInfix ("$" + "{") s;
# is_quoteable = s: (only_printable_ascii s) && (!lib.hasInfix ("$" + "{") s);
sieve_raw_escape_string =
s:
if !only_printable_ascii s then
builtins.trace s
throw "s failed only_printable_ascii check"
builtins.trace s throw "s failed only_printable_ascii check"
else
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s
;
replaceStrings [ ''"'' ''\'' "\n" "\r" ] [ ''\"'' ''\\'' ''\n'' ''\r'' ] s;
sieve_encode_string =
{ allow_vars, for_debug_comment, with_quotes }: s:
{
allow_vars,
for_debug_comment,
with_quotes,
}:
s:
assert isString s;
assert allow_vars || for_debug_comment || (!has_vars s);
assert allow_vars || for_debug_comment || (!has_vars s);
let
a = sieve_raw_escape_string s;
b = if for_debug_comment then replaceStrings [ ''*/'' ] [ ''*\/'' ] a else a;
res = if with_quotes then ''"${b}"'' else b;
in res
;
sieve_quote_string = sieve_encode_string { allow_vars = false; for_debug_comment = false; with_quotes = true; };
sieve_quote_string_with_interp = sieve_encode_string { allow_vars = true; for_debug_comment = false; with_quotes = true; };
in
res;
sieve_quote_string = sieve_encode_string {
allow_vars = false;
for_debug_comment = false;
with_quotes = true;
};
sieve_quote_string_with_interp = sieve_encode_string {
allow_vars = true;
for_debug_comment = false;
with_quotes = true;
};
is_valid_long_ident = is_match "[a-z_][a-z0-9_]*";
is_number_ident = is_match "[0-9]*";
is_valid_ident = s: (is_valid_long_ident s) || (is_number_ident s);
interp = ident:
interp =
ident:
assert isString ident;
assert is_valid_ident ident;
"$" + "{${ident}}"
;
"$" + "{${ident}}";
dest = "envelope_to";
dest_domain = "envelope_to_domain";
set_envelope = ''
@@ -133,139 +146,155 @@ let
'';
envelope_is = key: ''string :is "${interp dest}" ${sieve_quote_string key}'';
envelope_domain_is = key: ''string :is "${interp dest_domain}" ${sieve_quote_string key}'';
sieve_encode_list = xs:
sieve_encode_list =
xs:
assert isListWhere xs isString;
"[ ${mapConcatSep ", " sieve_encode xs} ]"
;
sieve_encode = val:
if isString val then sieve_quote_string val
else if isList val then sieve_encode_list val
else assert "dunno what to do with this"; null
;
"[ ${mapConcatSep ", " sieve_encode xs} ]";
sieve_encode =
val:
if isString val then
sieve_quote_string val
else if isList val then
sieve_encode_list val
else
assert "dunno what to do with this";
null;
sieve_debug_list = xs: "[ ${mapConcat (s: (sieve_debug s) + " ") xs}]";
sieve_debug_attrs = attrs:
let
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
pairStrs = mapAttrsToList toPairStr attrs;
pairsStr = concatStrings pairStrs;
in
"{ ${pairsStr}}"
;
sieve_debug = val:
if isString val then sieve_encode_string { allow_vars = true; for_debug_comment = true; with_quotes = true; } val
else if (isInt val) || (isFloat val) then toString val
sieve_debug_attrs =
attrs:
let
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
pairStrs = mapAttrsToList toPairStr attrs;
pairsStr = concatStrings pairStrs;
in
"{ ${pairsStr}}";
sieve_debug =
val:
if isString val then
sieve_encode_string {
allow_vars = true;
for_debug_comment = true;
with_quotes = true;
} val
else if (isInt val) || (isFloat val) then
toString val
else if (isBool val) then
(if val then "true" else "false")
else if isNull val then "null"
else if isList val then sieve_debug_list val
else if isAttrs val then sieve_debug_attrs val
else assert "dunno what to do with this"; null
;
pure_flags_impl = flags: conditions:
assert isListWhere flags isString;
assert isListWhere conditions isString;
assert (length flags) > 0;
assert (length conditions) > 0;
let
argAttrs = { inherit flags conditions; };
firstFlag = head flags;
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
in
''
# pure_flags ${sieve_debug argAttrs};
removeflag ${sieve_quote_string firstFlag};
if ${combined_condition} {
${record_action "pure_flags ${concatStringsSep " " flags}"}
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)}
}
# pure_flags end
'';
pure_flags = flags: conditions:
else if isNull val then
"null"
else if isList val then
sieve_debug_list val
else if isAttrs val then
sieve_debug_attrs val
else
assert "dunno what to do with this";
null;
pure_flags_impl =
flags: conditions:
assert isListWhere flags isString;
assert isListWhere conditions isString;
assert (length flags) > 0;
assert (length conditions) > 0;
let
argAttrs = { inherit flags conditions; };
firstFlag = head flags;
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
in
''
# pure_flags ${sieve_debug argAttrs};
removeflag ${sieve_quote_string firstFlag};
if ${combined_condition} {
${record_action "pure_flags ${concatStringsSep " " flags}"}
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)}
}
# pure_flags end
'';
pure_flags =
flags: conditions:
assert stringOrList flags;
assert stringOrList conditions;
pure_flags_impl (listify flags) (listify conditions)
;
exists_impl = headers:
pure_flags_impl (listify flags) (listify conditions);
exists_impl =
headers:
assert isListWhere headers isString;
if headers == [] then
if headers == [ ] then
"/* exists START: called with empty array */ false /* exists END */"
else
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */"
;
exists = headers:
"/* exists START */ exists ${sieve_encode_list headers} /* exists END */";
exists =
headers:
assert stringOrList headers;
exists_impl (listify headers)
;
header_generic = match_kind: header_s: match_es:
exists_impl (listify headers);
header_generic =
match_kind: header_s: match_es:
assert stringOrList header_s;
assert stringOrList match_es;
''/* header_generic START */ header ${match_kind} ${sieve_encode header_s} ${sieve_encode match_es} /* header_generic END */''
;
''/* header_generic START */ header ${match_kind} ${sieve_encode header_s} ${sieve_encode match_es} /* header_generic END */'';
header_matches = header_generic ":matches";
header_is = header_generic ":is";
subject_generic = match_kind: match_es: header_generic match_kind "Subject" match_es;
subject_matches = subject_generic ":matches";
subject_is = subject_generic ":is";
environment_generic = match_kind: environment_name_s: match_es:
environment_generic =
match_kind: environment_name_s: match_es:
assert stringOrList environment_name_s;
assert stringOrList match_es;
"environment ${match_kind} ${sieve_encode environment_name_s} ${sieve_encode match_es}"
;
"environment ${match_kind} ${sieve_encode environment_name_s} ${sieve_encode match_es}";
environment_matches = environment_generic ":matches";
environment_is = environment_generic ":is";
from_is = addr_list:
from_is =
addr_list:
assert stringOrList addr_list;
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */''
;
var_is = var_name: rhs:
''/* from_is START */ address :is :all "From" ${sieve_encode addr_list} /* from_is END */'';
var_is =
var_name: rhs:
assert isString var_name;
assert stringOrList rhs;
''string :is "''${${var_name}}" ${sieve_encode rhs}''
;
''string :is "''${${var_name}}" ${sieve_encode rhs}'';
var_is_true = var_name: var_is var_name "1";
var_is_false = var_name: not (var_is_true var_name);
set_with_interp = var_name: new_val:
set_with_interp =
var_name: new_val:
assert isString var_name;
assert is_valid_ident var_name;
assert isString new_val;
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};"
;
set = var_name: new_val:
"set ${sieve_encode var_name} ${sieve_quote_string_with_interp new_val};";
set =
var_name: new_val:
assert isString var_name;
assert is_valid_ident var_name;
assert isString new_val;
"set ${sieve_encode var_name} ${sieve_encode new_val};"
;
set_bool_var = var_name: bool_val:
"set ${sieve_encode var_name} ${sieve_encode new_val};";
set_bool_var =
var_name: bool_val:
assert isBool bool_val;
set var_name (if bool_val then "1" else "0")
;
over_test_list = name: test_list:
set var_name (if bool_val then "1" else "0");
over_test_list =
name: test_list:
assert isListWhere test_list isString;
''
${name}(
${concatStringsSep ",\n" test_list}
)
''
;
'';
anyof = over_test_list "anyof";
allof = over_test_list "allof";
not = test: "not ${test}";
record_action = action_desc:
record_action =
action_desc:
assert isString action_desc;
''addheader "X-Vacu-Action" ${sieve_encode action_desc};''
;
fileinto = folder:
''addheader "X-Vacu-Action" ${sieve_encode action_desc};'';
fileinto =
folder:
assert isString folder;
''
${record_action "fileinto ${folder}"}
fileinto :create ${sieve_encode folder};
''
;
ihave = extension_name_s:
'';
ihave =
extension_name_s:
assert stringOrList extension_name_s;
"ihave ${sieve_encode extension_name_s}"
;
"ihave ${sieve_encode extension_name_s}";
email_filters = map (e: ''
elsif ${envelope_is e} { # item of email_filters
${record_action "email_filters fileinto ${mk_email_folder_name e}"}
@@ -291,7 +320,9 @@ let
${set_with_interp var (interp "1")}
}
else {
${lib.optionalString warn_if_unset (maybe_debug "info: Could not set ${var} from condition ${condition}, setting to default(${default})")}
${lib.optionalString warn_if_unset (
maybe_debug "info: Could not set ${var} from condition ${condition}, setting to default(${default})"
)}
${set var default}
}
# set_from END
@@ -305,13 +336,11 @@ let
condition = ''environment :matches ${sieve_quote_string item} "*"'';
inherit var;
};
maybe_debug = msg:
''
if ${ihave "vnd.dovecot.debug"} {
debug_log ${sieve_quote_string_with_interp msg};
}
''
;
maybe_debug = msg: ''
if ${ihave "vnd.dovecot.debug"} {
debug_log ${sieve_quote_string_with_interp msg};
}
'';
sieve_text = ''
require [
"fileinto",
@@ -325,22 +354,33 @@ let
"ihave"
];
if ${allof [
(ihave "imapsieve")
(environment_matches "imap.user" "*")
(environment_matches "location" "MS")
(environment_matches "phase" "post")
]} {
if ${
allof [
(ihave "imapsieve")
(environment_matches "imap.user" "*")
(environment_matches "location" "MS")
(environment_matches "phase" "post")
]
} {
${set_bool_var "in_imap" true}
} else {
${set_bool_var "in_imap" false}
}
if ${var_is_true "in_imap"} {
if ${not (allof [
(environment_is "imap.cause" ["APPEND" "COPY" ""])
(environment_is "imap.mailbox" ["MagicRefilter" ""])
])} {
if ${
not (allof [
(environment_is "imap.cause" [
"APPEND"
"COPY"
""
])
(environment_is "imap.mailbox" [
"MagicRefilter"
""
])
])
} {
${maybe_debug "NOT doing anything cuz imap.cause and/or imap.mailbox isn't right"}
stop;
}
@@ -358,9 +398,7 @@ let
condition = ''currentdate :matches "iso8601" "*"'';
var = "datetime";
}}
${set_with_interp "sieved_message"
''at ''${datetime} by ${config.vacu.versionId} loc ''${env_location} phase ''${env_phase} user ''${env_imap_user} email ''${env_imap_email} cause ''${env_imap_cause} mailbox ''${env_imap_mailbox} changedflags ''${env_imap_changedflags} envelope ''${dest}''
}
${set_with_interp "sieved_message" ''at ''${datetime} by ${config.vacu.versionId} loc ''${env_location} phase ''${env_phase} user ''${env_imap_user} email ''${env_imap_email} cause ''${env_imap_cause} mailbox ''${env_imap_mailbox} changedflags ''${env_imap_changedflags} envelope ''${dest}''}
${maybe_debug ''X-Vacu-Sieved: ''${sieved_message}''}
if ${ihave "envelope"} {
@@ -382,43 +420,76 @@ let
removeflag "ignore";
removeflag "not-spamish";
${pure_flags ["amazon-ignore" "ignore"] [
(envelope_is "amznbsns@shelvacu.com")
(subject_matches [ "Your Amazon.com order has shipped*" "Your Amazon.com order of * has shipped!" ])
]}
${pure_flags ["bandcamp-ignore" "ignore"] [
(envelope_is "bandcamp@shelvacu.com")
(subject_matches [ "* just announced a listening party on Bandcamp" "New items from *" "Starting in *" "New from *" ])
]}
${pure_flags ["ika-ignore" "ignore"] (envelope_is "ika@dis8.net")}
${pure_flags ["ally-statement" "ignore"] [
(envelope_is "ally@shelvacu.com")
(subject_is "Your latest statement is ready to view.")
]}
${pure_flags
[ "amazon-ignore" "ignore" ]
[
(envelope_is "amznbsns@shelvacu.com")
(subject_matches [
"Your Amazon.com order has shipped*"
"Your Amazon.com order of * has shipped!"
])
]
}
${pure_flags
[ "bandcamp-ignore" "ignore" ]
[
(envelope_is "bandcamp@shelvacu.com")
(subject_matches [
"* just announced a listening party on Bandcamp"
"New items from *"
"Starting in *"
"New from *"
])
]
}
${pure_flags [ "ika-ignore" "ignore" ] (envelope_is "ika@dis8.net")}
${pure_flags
[ "ally-statement" "ignore" ]
[
(envelope_is "ally@shelvacu.com")
(subject_is "Your latest statement is ready to view.")
]
}
${pure_flags "bloomberg" (envelope_is "bloomberg@shelvacu.com")}
${pure_flags ["money-stuff" "not-spamish"] [
(envelope_is "bloomberg@shelvacu.com")
''header :matches "From" "\"Matt Levine\" *"''
]}
${pure_flags
[ "money-stuff" "not-spamish" ]
[
(envelope_is "bloomberg@shelvacu.com")
''header :matches "From" "\"Matt Levine\" *"''
]
}
${pure_flags ["git" "not-spamish"] (exists ["X-GitHub-Reason" "X-GitLab-Project"])}
${pure_flags ["git-uninsane" "git" "not-spamish"] (envelope_is "git-uninsane@shelvacu.com")}
${pure_flags [ "git" "not-spamish" ] (exists [
"X-GitHub-Reason"
"X-GitLab-Project"
])}
${pure_flags [ "git-uninsane" "git" "not-spamish" ] (envelope_is "git-uninsane@shelvacu.com")}
${pure_flags ["discourse" "not-spamish"] (exists "X-Discourse-Post-Id")}
${pure_flags ["agora" "not-spamish"] (envelope_is "agora@shelvacu.com")}
${pure_flags ["postgres-list" "not-spamish"] (header_matches "List-Id" "<*.lists.postgresql.org>")}
${pure_flags ["secureaccesswa" "not-spamish"] (from_is "help@secureaccess.wa.gov")}
${pure_flags ["letsencrypt-mailing-list" "not-spamish"] (envelope_is "lets-encrypt-mailing-list@shelvacu.com")}
${pure_flags ["jmp-news" "not-spamish"] (header_matches "List-Id" "*<jmp-news.soprani.ca>")}
${pure_flags ["tf2wiki" "not-spamish"] [
(envelope_is "tf2wiki@shelvacu.com")
(from_is "noreply@wiki.teamfortress.com")
]}
${pure_flags [ "discourse" "not-spamish" ] (exists "X-Discourse-Post-Id")}
${pure_flags [ "agora" "not-spamish" ] (envelope_is "agora@shelvacu.com")}
${pure_flags [ "postgres-list" "not-spamish" ] (
header_matches "List-Id" "<*.lists.postgresql.org>"
)}
${pure_flags [ "secureaccesswa" "not-spamish" ] (from_is "help@secureaccess.wa.gov")}
${pure_flags [ "letsencrypt-mailing-list" "not-spamish" ] (
envelope_is "lets-encrypt-mailing-list@shelvacu.com"
)}
${pure_flags [ "jmp-news" "not-spamish" ] (header_matches "List-Id" "*<jmp-news.soprani.ca>")}
${pure_flags
[ "tf2wiki" "not-spamish" ]
[
(envelope_is "tf2wiki@shelvacu.com")
(from_is "noreply@wiki.teamfortress.com")
]
}
${pure_flags "gmail-fwd" (envelope_is "gmailfwd-fc2e10bec8b2@shelvacu.com")}
${pure_flags "aliexpress" (from_is ["transaction@notice.aliexpress.com" "aliexpress@notice.aliexpress.com"])}
${pure_flags "aliexpress" (from_is [
"transaction@notice.aliexpress.com"
"aliexpress@notice.aliexpress.com"
])}
removeflag "auto-marked-read";
if hasflag "ignore" {

View File

@@ -31,6 +31,6 @@
vacu.packages = [
"yt-dlp"
"radicle-node"
"ncurses" #want this everywhere for clear/reset commands, but its included by default in nixos so only need ta specify here
"ncurses" # want this everywhere for clear/reset commands, but its included by default in nixos so only need ta specify here
];
}

View File

@@ -1,8 +1,4 @@
{
config,
lib,
...
}:
{ config, lib, ... }:
{
imports = [ ./lean.nix ];
opts = {
@@ -23,7 +19,10 @@
lua_ls.enable = true;
nixd = {
enable = true;
cmd = [ (lib.getExe config.plugins.lsp.servers.nixd.package) "--log=error" ];
cmd = [
(lib.getExe config.plugins.lsp.servers.nixd.package)
"--log=error"
];
};
pyright.enable = true;
rust_analyzer = {

View File

@@ -9,7 +9,8 @@ let
lean = inputs.self.packages.${system}.leanLatest;
in
{
imports = []
imports =
[ ]
++ (lib.optional (!unstable) {
plugins.lean.leanPackage = lean;
plugins.lean.mappings = true;
@@ -17,7 +18,6 @@ in
++ (lib.optional unstable {
dependencies.lean.enable = false;
plugins.lean.settings.mappings = true;
})
;
});
plugins.lean.enable = true;
}

View File

@@ -8,7 +8,7 @@
git,
libdbusmenu,
# options passed to the thunderbird builder, but with betterbird defaults
# options passed to the thunderbird builder, but with betterbird defaults
privacySupport ? true,
requireSigning ? false,
allowAddonSideload ? true,
@@ -33,25 +33,28 @@ let
tag = version;
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
};
patchesFromThunderbird = runCommandNoCC "betterbird-patches-from-network" {
outputHashAlgo = "sha256";
outputHashMode = "recursive";
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
} ''
set -xev
mkdir -p $out
fetchFromSeries() {
local seriesFile="$1"
shift
filteredSeries="$(mktemp)"
cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries"
while IFS=" " read -r filename url; do
${lib.getExe curl} -v -k "$url" --output "$out/$filename"
done < "$filteredSeries"
}
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU}
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series
'';
patchesFromThunderbird =
runCommandNoCC "betterbird-patches-from-network"
{
outputHashAlgo = "sha256";
outputHashMode = "recursive";
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
}
''
set -xev
mkdir -p $out
fetchFromSeries() {
local seriesFile="$1"
shift
filteredSeries="$(mktemp)"
cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries"
while IFS=" " read -r filename url; do
${lib.getExe curl} -v -k "$url" --output "$out/$filename"
done < "$filteredSeries"
}
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU}
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series
'';
mozilla_src = fetchhg {
name = "mozilla--mozilla";
url = stuff.MOZILLA_REPO;
@@ -95,54 +98,64 @@ let
applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm
'';
in
(thunderbird-128-unwrapped.override {
inherit privacySupport requireSigning allowAddonSideload;
}).overrideAttrs (old: rec {
pname = "betterbird";
inherit version;
name = "${pname}-${version}";
(thunderbird-128-unwrapped.override { inherit privacySupport requireSigning allowAddonSideload; })
.overrideAttrs
(old: rec {
pname = "betterbird";
inherit version;
name = "${pname}-${version}";
buildInputs = (old.buildInputs or []) ++ [ libdbusmenu ];
buildInputs = (old.buildInputs or [ ]) ++ [ libdbusmenu ];
src = replacement_src;
src = replacement_src;
configureFlags = (old.configureFlags or []) ++ [
# "--enable-application=comm/mail"
"--with-branding=comm/mail/branding/betterbird"
# "--disable-updater"
# "--disable-crashreporter"
# "--enable-tests"
# "--without-wasm-sandboxed-libraries"
# "--with-unsigned-addon-scopes=app,system"
# "--allow-addon-sideload"
# "--enable-default-toolkit=cairo-gtk3-wayland"
# "--enable-official-branding"
];
configureFlags = (old.configureFlags or [ ]) ++ [
# "--enable-application=comm/mail"
"--with-branding=comm/mail/branding/betterbird"
# "--disable-updater"
# "--disable-crashreporter"
# "--enable-tests"
# "--without-wasm-sandboxed-libraries"
# "--with-unsigned-addon-scopes=app,system"
# "--allow-addon-sideload"
# "--enable-default-toolkit=cairo-gtk3-wayland"
# "--enable-official-branding"
];
preConfigure = (old.preConfigure or "") + ''
# Disable enforcing that add-ons are signed.
export MOZ_REQUIRE_SIGNING=
export MOZ_REQUIRE_ADDON_SIGNING=0
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
# Set the WM_CLASS referenced in the .desktop file.
export MOZ_APP_REMOTINGNAME=eu.betterbird.Betterbird
# Needed to enable breakpad in application.ini
# The preceding comment appears all over the Mozilla repos, however it is misleading.
# "Official" (server) builds, as opposed to local builds, should have nothing to do
# with "breakpad" (https://chromium.googlesource.com/breakpad/) crash reporting.
# In any case, we don't want a local build.
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING= # No telemetry.
# Used for Linux to create small launcher executable for file browsers.
# See https://hg.mozilla.org/mozilla-central/rev/3cbbfc5127e4 for details.
export MOZ_NO_PIE_COMPAT=1
'';
preConfigure =
(old.preConfigure or "")
+ ''
# Disable enforcing that add-ons are signed.
export MOZ_REQUIRE_SIGNING=
export MOZ_REQUIRE_ADDON_SIGNING=0
passthru = (old.passthru or {}) // { inherit betterbirdPatches mozilla_src comm_src replacement_src patchesFromThunderbird; };
})
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
# Set the WM_CLASS referenced in the .desktop file.
export MOZ_APP_REMOTINGNAME=eu.betterbird.Betterbird
# Needed to enable breakpad in application.ini
# The preceding comment appears all over the Mozilla repos, however it is misleading.
# "Official" (server) builds, as opposed to local builds, should have nothing to do
# with "breakpad" (https://chromium.googlesource.com/breakpad/) crash reporting.
# In any case, we don't want a local build.
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING= # No telemetry.
# Used for Linux to create small launcher executable for file browsers.
# See https://hg.mozilla.org/mozilla-central/rev/3cbbfc5127e4 for details.
export MOZ_NO_PIE_COMPAT=1
'';
passthru = (old.passthru or { }) // {
inherit
betterbirdPatches
mozilla_src
comm_src
replacement_src
patchesFromThunderbird
;
};
})

View File

@@ -1,14 +1,15 @@
# shellcheck shell=bash
translateWetransferLinks() {
local old_urlsArray=("${urls[*]}")
local new_urlsArray=()
for this_url in "${old_urlsArray[@]}"; do
if [[ "$this_url" = https://we.tl/* ]] || [[ "$this_url" = https://wetransfer.com/downloads/* ]]; then
local new_url
new_url="$("$transferweeBin" -v -g download "$this_url")"
new_urlsArray+=("$new_url")
else
new_urlsArray+=("$this_url")
fi
if [[ $this_url == https://we.tl/* ]] || [[ $this_url == https://wetransfer.com/downloads/* ]]; then
local new_url
new_url="$("${transferweeBin:?}" -v -g download "$this_url")"
new_urlsArray+=("$new_url")
else
new_urlsArray+=("$this_url")
fi
done
urls="${new_urlsArray[*]}"
}

View File

@@ -1,7 +1,9 @@
self: _super: {
fetchurlWithWetransfer = args: (self.fetchurl args).overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ ./convert-wetransfer-links.sh ];
env.transferweeBin = self.lib.getExe self.transferwee;
env.NIX_DEBUG = "6";
});
fetchurlWithWetransfer =
args:
(self.fetchurl args).overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ ./convert-wetransfer-links.sh ];
env.transferweeBin = self.lib.getExe self.transferwee;
env.NIX_DEBUG = "6";
});
}

View File

@@ -1,15 +1,13 @@
{
lean4,
fetchFromGitHub,
}:
lean4.overrideAttrs (final: prev: {
version = "4.19.0-rc3";
src = fetchFromGitHub {
owner = "leanprover";
repo = "lean4";
tag = "v4.19.0-rc3";
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
};
cmakeFlags = (prev.cmakeFlags or []) ++ [ "-DUSE_MIMALLOC=OFF" ];
})
{ lean4, fetchFromGitHub }:
lean4.overrideAttrs (
final: prev: {
version = "4.19.0-rc3";
src = fetchFromGitHub {
owner = "leanprover";
repo = "lean4";
tag = "v4.19.0-rc3";
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
};
cmakeFlags = (prev.cmakeFlags or [ ]) ++ [ "-DUSE_MIMALLOC=OFF" ];
}
)

View File

@@ -17,8 +17,9 @@ let
];
python = pkgs.python312.withPackages (_: libraries);
in
(pkgs.writers.writePython3Bin "dns-update" {
inherit libraries;
} pythonScript).overrideAttrs (old: {
passthru = (old.passthru or {}) // { inherit libraries python; };
})
(pkgs.writers.writePython3Bin "dns-update" { inherit libraries; } pythonScript).overrideAttrs
(old: {
passthru = (old.passthru or { }) // {
inherit libraries python;
};
})

View File

@@ -4,98 +4,98 @@ dkim_key: ENC[AES256_GCM,data:CZC/1U1cJUIyNhXAWp+YFJd0pZZKvZClJxOh3uZ3YyfEQBiK9n
dkim_pub: ENC[AES256_GCM,data:XLYRTAviK+r6DnRU4+lc58elI3FJ+FPsB1A5sQOk+pb+fNu7zFCiZdz/MwTVkE9izDP1Onv+VhV8sRgmxacTv4nW5GcukCrm3FmCp2jm6QF1/40/WRv6Lkbek0tV1bMOQPy9Zj8wdO9M05XCXUVXk4x17rj+lw8ApwJS2pJMoultMFx34tx2pNEnmO3MFtuBOxzeU2yP+NhF2sJNA62to78AiH5EblkoF0a6sUYk553U+sv3Ob0lo1nSv6c8zwl7y1WSNQnLK+/3WxSVGfePHVsVM8Zze1KFTVLQQggIzWTdcr7AgcTGbk3kaYCeucfQ60pVlqOyPnkJoUJ8HR1RSajFk6Ylzw0xBpY85qAXNT2YIRiq0HTUc1s5lD0luXLQEP+g+XUwZfzFRZgt1nWBlPmpbj2Ylj1FfrA7EXsIK9nyo+rf0qRn/4HusJATr9ddYmZdxwazl1FXkOKLHPyu1NlzwoTNSQQgMHlzxzUvrrv7+mI2nQvXRx82TSRytqrMvoBTF1NFX+pRjhNg9fcq0oPJ2ORqOVQsxzhLhB+tw7Cg+UHGWlnKnkqaKH1JDmOFyJDB96aPUnSQT2J8qkyb+hMBXz9mme8rZopkHrA4WyDXv3zpEi0P5Sj0DDwdRxKMdDdZ4hw79YQIrd63cIorN8XG6Icevb25LfekLEq/C2FS8+kADagyOM0uzCw2p/qacNz37ZNGqPK6gYkjnyoAfSm14zbgoLX/5dnf7eCuMatevTm4AcE53RawQfzz0YNJuEv5uqD/WUy+UIKHIwxPYY9FWBBPmH+8eaPC1mMPh54I444b39FwpnPU8GwxEPsjRg8TSnohawNmmhEWEpmlawEKw+C+BE6A2DmVJzyeBvVRwe/W6CPgyYxgSGWUuvfZFm1GrzwZDjCOEMRn7qMwMBxh1nr2BOAiNxA38UtsymaZO5ZOknClWlKIkIFl8NJdVITNNsI48KMuSY20o1puzkxMaAUH3OrGEhtoHrEOeIq+KCFzH2gZo6L5hbv9CHM7QgCYsbtVIMwL+cRZZaSNubS3K48OmWJnHNuqkcrSI4lqfjLhz1DbnQ==,iv:/cNMmlpq9LSOk0MwVq8NaWvp47q68lKWTx4s5nkwF5c=,tag:ZNX+yZsSxdhFsavDpX380g==,type:str]
relay_creds: ENC[AES256_GCM,data:o0FIKyqYHo1mndY+TC6TopipDlZMoyePPPRF62+WVegWjnz+dG83WTzIduJ6qdzlkBH0tgYfau7aIzYaDWZAd935efxvwTMl8lot0xTa8SqAYxQKDkTcpUhaHtu9wlpaqv31vzPdGUJbI17e9ZPdMEPRNaEYQkYqP2YoagO17WRbzIOax+XTP08pyVJChDG++aYlkuScOXQyM830hDy2xCYA9OHN4BeyU5mh6W0BiXLYIp9oOh0y1We59CUKeo0S,iv:JHgLeQO6XE5VYsoPU4YrI+LIaWSETvfnnwjrlTc1n0g=,tag:cWafuECJy2Gv5BMGKG1NOw==,type:str]
sops:
age:
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2
MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi
VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6
bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P
hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1
cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj
aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD
RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO
oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4
RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx
UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx
UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor
3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg==
-----END AGE ENCRYPTED FILE-----
- recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm
c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs
QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI
Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw
UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA==
-----END AGE ENCRYPTED FILE-----
- recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx
UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK
V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0
ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX
jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A==
-----END AGE ENCRYPTED FILE-----
- recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp
Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3
U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM
bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp
7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5
ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz
d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0
MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw
N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw==
-----END AGE ENCRYPTED FILE-----
- recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj
eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ
bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr
VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a
go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg==
-----END AGE ENCRYPTED FILE-----
- recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU
aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5
eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ
MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q
4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU
U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t
NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2
bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq
UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-16T00:39:52Z"
mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.1
age:
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2
MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi
VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6
bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P
hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1
cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj
aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD
RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO
oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4
RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx
UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx
UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor
3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg==
-----END AGE ENCRYPTED FILE-----
- recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm
c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs
QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI
Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw
UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA==
-----END AGE ENCRYPTED FILE-----
- recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx
UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK
V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0
ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX
jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A==
-----END AGE ENCRYPTED FILE-----
- recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp
Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3
U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM
bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp
7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5
ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz
d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0
MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw
N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw==
-----END AGE ENCRYPTED FILE-----
- recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj
eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ
bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr
VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a
go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg==
-----END AGE ENCRYPTED FILE-----
- recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU
aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5
eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ
MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q
4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU
U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t
NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2
bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq
UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-16T00:39:52Z"
mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.1

View File

@@ -71,31 +71,34 @@ let
mkdir -p $out/liam
SOPS_AGE_KEY="${testAgeSecret}" ${pkgs.sops}/bin/sops --verbose -e --age "$(echo "${testAgeSecret}" | ${pkgs.age}/bin/age-keygen -y)" ${sopsTestSecretsYaml} --output-type yaml > $out/liam/main.yaml
'';
mailtestModule = {
config,
pkgs,
lib,
...
}:
let
libraries = with pkgs.python3Packages; [
imap-tools
requests
];
mkPkg = name: pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
# flake8: noqa
${builtins.readFile ./mailtest/${name}.py}
'';
in
{
options.vacu.mailtest = lib.mkOption { readOnly = true; };
config.vacu.mailtest = {
smtp = mkPkg "smtp";
imap = mkPkg "imap";
mailpit = mkPkg "mailpit";
mailtestModule =
{
config,
pkgs,
lib,
...
}:
let
libraries = with pkgs.python3Packages; [
imap-tools
requests
];
mkPkg =
name:
pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
# flake8: noqa
${builtins.readFile ./mailtest/${name}.py}
'';
in
{
options.vacu.mailtest = lib.mkOption { readOnly = true; };
config.vacu.mailtest = {
smtp = mkPkg "smtp";
imap = mkPkg "imap";
mailpit = mkPkg "mailpit";
};
config.environment.systemPackages = builtins.attrValues config.vacu.mailtest;
};
config.environment.systemPackages = builtins.attrValues config.vacu.mailtest;
};
in
{
name = "liam-receives-mail";
@@ -168,7 +171,12 @@ in
};
nodes.liam =
{ lib, inputs, config, ... }:
{
lib,
inputs,
config,
...
}:
{
imports = [
"${inputs.self}/common"
@@ -209,8 +217,10 @@ in
security.pki.certificateFiles = [ rootCA.certificatePath ];
vacu.liam.backup.keyPath = pkgs.writeText "test-borg-key" testBorgKey;
vacu.ssh.knownHosts."rsync.net".enable = lib.mkForce false; #remove known key so i can do a trust-on-first-use in the test
networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [ config.vacu.liam.backup.rsyncHost ];
vacu.ssh.knownHosts."rsync.net".enable = lib.mkForce false; # remove known key so i can do a trust-on-first-use in the test
networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [
config.vacu.liam.backup.rsyncHost
];
};
nodes.rsyncnet =
@@ -218,7 +228,7 @@ in
let
borgCfg = nodes.liam.vacu.liam.backup;
user = borgCfg.rsyncUser;
loginCommand = pkgs.writers.writeBashBin "special-rsync-login" {} ''
loginCommand = pkgs.writers.writeBashBin "special-rsync-login" { } ''
set -euo pipefail
if [ -n "$SSH_ORIGINAL_COMMAND" ]; then
echo "hi there"
@@ -255,7 +265,7 @@ in
};
environment.systemPackages = [
pkgs.borgbackup
(pkgs.writers.writeBashBin "borg-init" {} ''
(pkgs.writers.writeBashBin "borg-init" { } ''
set -euo pipefail
export BORG_PASSPHRASE=${lib.escapeShellArg testBorgKey}
export BORG_NEW_PASSPHRASE=${lib.escapeShellArg testBorgKey}
@@ -268,27 +278,26 @@ in
{ pkgs, lib, ... }:
{
imports = [ mailtestModule ];
environment.systemPackages = [
pkgs.wget
];
environment.systemPackages = [ pkgs.wget ];
networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress);
};
testScript =
let
rawFile = builtins.readFile ./testScript/main.py;
data = {
checkSieve = lib.getExe nodes.liam.vacu.checkSieve;
acmeTest = pkgs.writeText "acme-test" "test";
acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test";
relayIP = nodes.relay.networking.primaryIPAddress;
liamIP = nodes.liam.networking.primaryIPAddress;
inherit testBorgKey;
borgExe = lib.getExe pkgs.borgbackup;
};
dataJson = builtins.toJSON data;
res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile;
in res;
let
rawFile = builtins.readFile ./testScript/main.py;
data = {
checkSieve = lib.getExe nodes.liam.vacu.checkSieve;
acmeTest = pkgs.writeText "acme-test" "test";
acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test";
relayIP = nodes.relay.networking.primaryIPAddress;
liamIP = nodes.liam.networking.primaryIPAddress;
inherit testBorgKey;
borgExe = lib.getExe pkgs.borgbackup;
};
dataJson = builtins.toJSON data;
res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile;
in
res;
skipTypeCheck = true;
skipLint = true;

View File

@@ -6,18 +6,22 @@ import ssl
import json
from typing import NamedTuple
def info(msg: str):
print(msg, file=sys.stderr)
def mk_ctx():
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return ctx
def print_json(**kwargs):
print(json.dumps(kwargs))
parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str)
parser.add_argument("--insecure", default=False, action="store_true")
@@ -45,6 +49,7 @@ info(f"looking for {msg_magic}")
result: bool
matching_messages = []
try:
def connection() -> imap_tools.BaseMailBox:
return imap_tools.MailBox(args.host, ssl_context=mk_ctx()).login(
username, password
@@ -87,7 +92,7 @@ try:
assert uid is not None
info(f"about to move {uid} to {args.move_to}")
res = mailbox.move(uid, args.move_to)
assert res[1][1][1] is not None, "failed to move" # type: ignore
assert res[1][1][1] is not None, "failed to move" # type: ignore
info(f"done moving, res {res!r}")
with connection() as mailbox:
@@ -99,6 +104,7 @@ except imaplib.IMAP4.error as e:
else:
result = True
def mail_to_jsonish(m: MessageInFolder) -> dict:
return {
"folder": m.folder,
@@ -106,7 +112,8 @@ def mail_to_jsonish(m: MessageInFolder) -> dict:
"body": m.message.text.strip(),
}
print_json(
result = result,
messages = [mail_to_jsonish(m) for m in matching_messages],
result=result,
messages=[mail_to_jsonish(m) for m in matching_messages],
)

View File

@@ -3,9 +3,11 @@ import requests
import sys
import json
def info(msg: str):
print(msg, file=sys.stderr)
def print_json(**kwargs):
print(json.dumps(kwargs))
@@ -37,4 +39,4 @@ for message_data in mails["messages"]:
found_message = True
break
print_json(found_message = found_message)
print_json(found_message=found_message)

View File

@@ -4,18 +4,22 @@ import sys
import ssl
import json
def info(msg: str):
print(msg, file=sys.stderr)
def mk_ctx():
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return ctx
def print_json(**kwargs):
print(json.dumps(kwargs))
parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str)
parser.add_argument("--mailfrom", default="foo@example.com")
@@ -64,4 +68,4 @@ except smtplib.SMTPSenderRefused:
else:
result = True
print_json(result = result)
print_json(result=result)

View File

@@ -2,14 +2,17 @@ import sys
import ssl
import json
def info(msg: str):
print(msg, file=sys.stderr)
def mk_ctx():
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return ctx
def print_json(**kwargs):
print(json.dumps(kwargs))

View File

@@ -6,7 +6,7 @@ DATA_JSON = """
"""
if TYPE_CHECKING:
from hints import * # type: ignore
from hints import * # type: ignore
import json
import shlex
@@ -32,7 +32,7 @@ liam.wait_for_unit("dovecot2.service")
relay.wait_for_unit("mailpit.service")
# generate and exchange keys so they can talk to eachother
rsyncnet.wait_for_open_port(22);
rsyncnet.wait_for_open_port(22)
# liam.succeed("install -d --owner=autoborger --group=autoborger --mode=0770 /var/lib/auto-borg")
# # not succeed, it will fail because not authorized but that's okay
# liam.execute("sudo -u autoborger ssh -oBatchMode=yes -oStrictHostKeyChecking=accept-new fm2382.rsync.net")
@@ -40,7 +40,9 @@ liam.succeed("systemctl start auto-borg-gen-key.service")
# liam.wait_for_unit("auto-borg-gen-key.service")
liam_autoborger_key = liam.succeed("cat /var/lib/auto-borg/id_ed25519.pub").strip()
rsyncnet.succeed("install -d --owner=fm2382 --mode=0700 /home/fm2382/.ssh")
rsyncnet.succeed(f"install --owner=fm2382 --mode=0600 -T <(echo 'command=\"borg serve --restrict-to-repository /home/fm2382/borg-repos/liam-backup --append-only\",restrict {liam_autoborger_key}') /home/fm2382/.ssh/authorized_keys")
rsyncnet.succeed(
f"install --owner=fm2382 --mode=0600 -T <(echo 'command=\"borg serve --restrict-to-repository /home/fm2382/borg-repos/liam-backup --append-only\",restrict {liam_autoborger_key}') /home/fm2382/.ssh/authorized_keys"
)
rsyncnet.succeed("sudo -u fm2382 borg-init")
liam.succeed("systemctl start auto-borg.service")
# liam.wait_for_unit("auto-borg.service")
@@ -51,21 +53,25 @@ class ImapMessage(TypedDict):
flags: list[str]
body: str
class ImapResult(TypedDict):
result: bool
messages: list[ImapMessage]
def make_command(args: list) -> str:
return " ".join(map(shlex.quote, (map(str, args))))
Arg = str|bool|int
Arg = str | bool | int
Args = dict[str, Arg]
def dict_args_to_list(dict_args: Args) -> list[str]:
args:list[str] = []
args: list[str] = []
for k, v in dict_args.items():
dashed = k.replace("_","-")
dashed = k.replace("_", "-")
if isinstance(v, int) and not isinstance(v, bool):
v = str(v)
if isinstance(v, str) or (isinstance(v, bool) and v):
@@ -74,15 +80,17 @@ def dict_args_to_list(dict_args: Args) -> list[str]:
args.append(v)
return args
class LogEntry(TypedDict):
__CURSOR: str
_SYSTEMD_UNIT: str
MESSAGE: str
pass
DEFAULT_JOURNALCTL_OPTS: Args = {
"all": True,
"output": "json",
"all": True,
"output": "json",
}
# --json
@@ -94,22 +102,27 @@ DEFAULT_JOURNALCTL_OPTS: Args = {
#
# 3. Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing the raw bytes individually formatted as unsigned numbers.
def journalctl_log_entries(machine: Machine = liam, **kwargs: Arg) -> Generator[LogEntry, None, None]:
def journalctl_log_entries(
machine: Machine = liam, **kwargs: Arg
) -> Generator[LogEntry, None, None]:
with_defaults = {**kwargs, **DEFAULT_JOURNALCTL_OPTS}
args = ["journalctl", *dict_args_to_list(with_defaults)]
res = machine.succeed(make_command(args))
for line in res.splitlines():
data:LogEntry = json.loads(line)
data: LogEntry = json.loads(line)
assert isinstance(data, dict)
for _, v in data.items():
assert isinstance(v, str)
yield data
class ProcessingWaiter(contextlib.AbstractContextManager):
cursor: str = ""
timeout: int = 60
_postfix_smtpd_connections: set[str] = set()
_postfix_queue: set[str] = set()
def __init__(self, timeout=60):
most_recent_entry = list(journalctl_log_entries(lines=1))[0]
self.cursor = most_recent_entry["__CURSOR"]
@@ -128,7 +141,9 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
assert sl_pid is not None
if message.startswith("connect from"):
self._postfix_smtpd_connections.add(sl_pid)
if message.startswith("disconnect from") or message.startswith("lost connection"):
if message.startswith("disconnect from") or message.startswith(
"lost connection"
):
self._postfix_smtpd_connections.discard(sl_pid)
if unit == "postfix.service" and sl_ident == "postfix/qmgr":
queue_id = message.split(":")[0]
@@ -136,7 +151,10 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
self._postfix_queue.add(queue_id)
if message.endswith("removed"):
self._postfix_queue.discard(queue_id)
if len(self._postfix_smtpd_connections) == 0 and len(self._postfix_queue) == 0:
if (
len(self._postfix_smtpd_connections) == 0
and len(self._postfix_queue) == 0
):
return
else:
liam.sleep(1)
@@ -144,32 +162,28 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
def __exit__(self, _a, _b, _c):
self.wait_until_finished()
class TesterThing():
class TesterThing:
uuid: str = ""
default_smtp: Args = {}
default_imap: Args = {}
default_mailpit: Args = {}
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: Arg):
def __init__(
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: Arg
):
self.uuid = str(uuid.uuid4())
self.default_smtp = {
"rcptto": "someone@example.com",
"host": f"{liam_ip}",
**common,
**smtp
}
self.default_imap = {
"host": f"{liam_ip}",
**common,
**imap
}
self.default_mailpit = {
"mailpit-url": f"http://{relay_ip}:8025",
**mailpit
**smtp,
}
self.default_imap = {"host": f"{liam_ip}", **common, **imap}
self.default_mailpit = {"mailpit-url": f"http://{relay_ip}:8025", **mailpit}
def run_expecting_json(self, name: str, **kwargs: Arg) -> dict[str, Any]:
args:list[str] = [name, *dict_args_to_list(kwargs)]
args: list[str] = [name, *dict_args_to_list(kwargs)]
print(f"running {args!r}")
with ProcessingWaiter():
res = checker.succeed(make_command(args))
@@ -195,35 +209,37 @@ class TesterThing():
def run_imap(self, **kwargs: Arg) -> ImapResult:
args = {"message_magic": self.uuid, **self.default_imap, **kwargs}
res_unty = self.run_expecting_json("mailtest-imap", **args)
res_ty: ImapResult = res_unty # type: ignore
res_ty: ImapResult = res_unty # type: ignore
return res_ty
def imap_expect(self, mailbox: str|None = None, flags: list[str] = [], **kwargs: Arg) -> Self:
def imap_expect(
self, mailbox: str | None = None, flags: list[str] = [], **kwargs: Arg
) -> Self:
res = self.run_imap(**kwargs)
assert res['result']
assert len(res['messages']) == 1
message = res['messages'][0]
assert res["result"]
assert len(res["messages"]) == 1
message = res["messages"][0]
if mailbox is not None:
assert message['folder'] == mailbox
assert message["folder"] == mailbox
for flag in flags:
assert flag in message['flags']
assert flag in message["flags"]
return self
def imap_found(self, **kwargs: Arg) -> Self:
return self.imap_expect(mailbox = None, flags = [], **kwargs)
return self.imap_expect(mailbox=None, flags=[], **kwargs)
def imap_found_in(self, mailbox: str, **kwargs: Arg) -> Self:
return self.imap_expect(mailbox = mailbox, flags = [], **kwargs)
return self.imap_expect(mailbox=mailbox, flags=[], **kwargs)
def imap_move_to(self, dest: str, **kwargs: Arg) -> Self:
res = self.run_imap(move_to=dest, **kwargs)
assert res['result']
assert res["result"]
return self
def run_mailpit(self, **kwargs: Arg) -> bool:
args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs}
res = self.run_expecting_json("mailtest-mailpit", **args)
return res['found_message']
return res["found_message"]
def mailpit_not_received(self, **kwargs: Arg) -> Self:
received = self.run_mailpit(**kwargs)
@@ -235,57 +251,78 @@ class TesterThing():
assert received
return self
class Defaults():
class Defaults:
default_smtp: Args = {}
default_imap: Args = {}
default_mailpit: Args = {}
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool):
self.default_smtp = {
**common,
**smtp
}
self.default_imap = {
**common,
**imap
}
self.default_mailpit = {
**mailpit
}
def make_tester(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool) -> TesterThing:
def __init__(
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
):
self.default_smtp = {**common, **smtp}
self.default_imap = {**common, **imap}
self.default_mailpit = {**mailpit}
def make_tester(
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
) -> TesterThing:
return TesterThing(
smtp = {**self.default_smtp, **common, **smtp},
imap = {**self.default_imap, **common, **imap},
mailpit = {**self.default_mailpit, **common, **mailpit},
smtp={**self.default_smtp, **common, **smtp},
imap={**self.default_imap, **common, **imap},
mailpit={**self.default_mailpit, **common, **mailpit},
)
# The order of these shouldn't matter, other than what fails first. Whatever is at the top is probably whatever I was working on most recently.
d = Defaults(smtp = {"submission": True, "rcptto": "someone@example.com"}, username = "vacustore")
d.make_tester(smtp = {"mailfrom": "robot@vacu.store"}).smtp_accepted().mailpit_received()
d.make_tester(smtp = {"mailfrom": "foobar@vacu.store"}).smtp_rejected()
d.make_tester(smtp = {"mailfrom": "abc@shelvacu.com"}).smtp_rejected()
d = Defaults(smtp = {"mailfrom": "whoeve2@example.com", "rcptto": "sieve2est@shelvacu.com"}, username = "shelvacu")
# The order of these shouldn't matter, other than what fails first. Whatever is at the top is probably whatever I was working on most recently.
d = Defaults(
smtp={"submission": True, "rcptto": "someone@example.com"}, username="vacustore"
)
d.make_tester(smtp={"mailfrom": "robot@vacu.store"}).smtp_accepted().mailpit_received()
d.make_tester(smtp={"mailfrom": "foobar@vacu.store"}).smtp_rejected()
d.make_tester(smtp={"mailfrom": "abc@shelvacu.com"}).smtp_rejected()
d = Defaults(
smtp={"mailfrom": "whoeve2@example.com", "rcptto": "sieve2est@shelvacu.com"},
username="shelvacu",
)
# test refilter
d.make_tester().smtp_accepted().imap_move_to("MagicRefilter").imap_found_in("com.shelvacu")
d.make_tester().smtp_accepted().imap_move_to("MagicRefilter").imap_found_in(
"com.shelvacu"
)
# refilter doesnt activate on other folders
d.make_tester().smtp_accepted().imap_move_to("testFolder").imap_found_in("testFolder")
d.make_tester().smtp_accepted().imap_move_to("INBOX").imap_found_in("INBOX")
# test the sieve script is working
d.make_tester().smtp_accepted().imap_found_in("com.shelvacu")
# refilter doesnt activate on julie's
d.make_tester(username="julie").smtp_accepted(rcptto="julie@shelvacu.com").imap_move_to("MagicRefilter").imap_found_in("MagicRefilter")
d.make_tester(username="julie").smtp_accepted(rcptto="julie@shelvacu.com").imap_move_to(
"MagicRefilter"
).imap_found_in("MagicRefilter")
d = Defaults(username = "shelvacu")
d.make_tester().smtp_accepted(mailfrom="asshole-spammer@example.com", rcptto="whatever@shelvacu.com", header="List-unsubscribe: whatever").imap_expect(mailbox="com.shelvacu.#spamish", flags=["spamish"])
d.make_tester().smtp_accepted(mailfrom="shipment-tracking@amazon.com", rcptto="amznbsns@shelvacu.com", subject="Your Amazon.com order has shipped (#123-1234)").imap_expect(mailbox="com.shelvacu", flags=["\\Seen","auto-marked-read","amazon-ignore"])
d = Defaults(username="shelvacu")
d.make_tester().smtp_accepted(
mailfrom="asshole-spammer@example.com",
rcptto="whatever@shelvacu.com",
header="List-unsubscribe: whatever",
).imap_expect(mailbox="com.shelvacu.#spamish", flags=["spamish"])
d.make_tester().smtp_accepted(
mailfrom="shipment-tracking@amazon.com",
rcptto="amznbsns@shelvacu.com",
subject="Your Amazon.com order has shipped (#123-1234)",
).imap_expect(
mailbox="com.shelvacu", flags=["\\Seen", "auto-marked-read", "amazon-ignore"]
)
TesterThing().smtp_accepted(rcptto="shelvacu@shelvacu.com", username="shelvacu", smtp_starttls=True)
TesterThing().smtp_accepted(
rcptto="shelvacu@shelvacu.com", username="shelvacu", smtp_starttls=True
)
d = Defaults(smtp = {"submission": True, "rcptto": "foo@example.com"}, username="shelvacu")
d = Defaults(
smtp={"submission": True, "rcptto": "foo@example.com"}, username="shelvacu"
)
d.make_tester().smtp_accepted(mailfrom="me@shelvacu.com").mailpit_received()
d.make_tester().smtp_accepted(mailfrom="me@dis8.net" ).mailpit_not_received()
d.make_tester().smtp_accepted(mailfrom="me@dis8.net").mailpit_not_received()
# julie's emails should NOT get sieve'd like mine
d = Defaults(username="julie")
@@ -302,7 +339,7 @@ d.make_tester().smtp_accepted(rcptto="julie@shelvacu.com").imap_found()
d.make_tester().smtp_accepted(rcptto="sales@theviolincase.com").imap_found()
d.make_tester().smtp_accepted(rcptto="superwow@theviolincase.com").imap_found()
#incoming mail cant be from known domains
# incoming mail cant be from known domains
TesterThing().smtp_rejected(mailfrom="bob@vacu.store")
TesterThing().smtp_rejected(mailfrom="shelvacu@shelvacu.com")
TesterThing().smtp_rejected(mailfrom="julie@shelvacu.com")
@@ -310,19 +347,27 @@ TesterThing().smtp_rejected(mailfrom="@vacu.store")
TesterThing().smtp_rejected(mailfrom="reject-spam-test@example.com")
#people cant send as the wrong person
d = Defaults(smtp = {"submission": True})
# people cant send as the wrong person
d = Defaults(smtp={"submission": True})
d.make_tester().smtp_rejected(mailfrom="julie@shelvacu.com", username="shelvacu")
d.make_tester().smtp_rejected(mailfrom="fubar@theviolincase.com", username="shelvacu")
d.make_tester().smtp_rejected(mailfrom="fubar@vacu.store", username="julie")
d = Defaults(smtp = {"submission": True, "rcptto": "foo@example.com"})
d = Defaults(smtp={"submission": True, "rcptto": "foo@example.com"})
d.make_tester().smtp_accepted(mailfrom="shelvacu@shelvacu.com", username="shelvacu")
d.make_tester().smtp_accepted(mailfrom="shelvacu@shelvacu.com", username="shelvacu@shelvacu.com", password="shelvacu")
d.make_tester().smtp_accepted(
mailfrom="shelvacu@shelvacu.com",
username="shelvacu@shelvacu.com",
password="shelvacu",
)
d.make_tester().smtp_accepted(mailfrom="foo@vacu.store", username="shelvacu")
d.make_tester().smtp_accepted(mailfrom="foo@vacu.store", username="shelvacu@shelvacu.com", password="shelvacu")
d.make_tester().smtp_accepted(
mailfrom="foo@vacu.store", username="shelvacu@shelvacu.com", password="shelvacu"
)
d.make_tester().smtp_accepted(mailfrom="foo@violingifts.com", username="julie")
d.make_tester().smtp_accepted(mailfrom="foo@violingifts.com", username="julie@shelvacu.com", password="julie")
d.make_tester().smtp_accepted(
mailfrom="foo@violingifts.com", username="julie@shelvacu.com", password="julie"
)
# now that there's a bunch of mail and logs and stuff, we can still run a borg backup, right?
liam.succeed("systemctl start auto-borg.service")

View File

@@ -1,9 +1,7 @@
{
...
}:
{ ... }:
{
# systemd.services.dovecot-backup-sftp-server = {
# enable = true;
#
#
# };
}

View File

@@ -22,7 +22,12 @@ in
restartIfChanged = true;
config =
{ lib, config, pkgs, ... }:
{
lib,
config,
pkgs,
...
}:
let
secrets_folder = "/var/lib/vaultwarden/secrets";
services = [ "vaultwarden.service" ];

View File

@@ -9,9 +9,9 @@
let
config = self.checks.x86_64-linux.${name}.config;
vlans = map (m: (
m.virtualisation.vlans ++
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
vlans = map (
m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
) (lib.attrValues config.nodes);
nodeHostNames =
let
@@ -19,13 +19,14 @@ let
in
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
pythonizeName = name:
pythonizeName =
name:
let
head = lib.substring 0 1 name;
tail = lib.substring 1 (-1) name;
in
(if builtins.match "[A-z_]" head == null then "_" else head) +
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
(if builtins.match "[A-z_]" head == null then "_" else head)
+ lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
uniqueVlans = lib.unique (builtins.concatLists vlans);
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;