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"; publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJJk3a190w/1TZkzVKORvz/kwyKmFY144lVeDFm80p17";
}; };
"rsync.net" = { "rsync.net" = {
extraHostNames = [ "rsn" "rsyncnet" "fm2382.rsync.net" ]; extraHostNames = [
"rsn"
"rsyncnet"
"fm2382.rsync.net"
];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdUkGe6kKn5ssz4WRZKjcws0InbQqZayenzk9obmP1z"; 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-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 = { vacu.packages = {
@@ -122,8 +125,7 @@ lib.mkMerge [
}; };
} }
{ {
vacu.packages = vacu.packages = with pkgs; [
with pkgs; [
bash bash
bzip2 bzip2
curl curl

View File

@@ -33,7 +33,8 @@ let
readOnly = true; 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 = config =
{ {
vacu.sourceTree = inputsOf inputs.self // { inherit inputs; }; vacu.sourceTree = inputsOf inputs.self // {
inherit inputs;
};
# vacu.sourceTree = pkgs.runCommand "inputs-tree" { } '' # vacu.sourceTree = pkgs.runCommand "inputs-tree" { } ''
# mkdir -p $out # mkdir -p $out
# ln -s ${inputs.self} $out/self # ln -s ${inputs.self} $out/self

View File

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

View File

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

View File

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

View File

@@ -13,8 +13,9 @@
background: #eee; background: #eee;
padding: 0 10px; padding: 0 10px;
} }
h1,h2,h3 { line-height: 1.2; } h1, h2, h3 {
line-height: 1.2;
}
form { form {
padding: 5px; padding: 5px;
@@ -22,21 +23,34 @@
border-radius: 3px; border-radius: 3px;
margin: 5px; margin: 5px;
} }
form label { display: block; } form label {
display: block;
}
</style> </style>
</head> </head>
<body> <body>
<h1>Jobs info</h1> <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> <p>
What I want in a job: What I want in a job:
<ul> <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>Prefer direct hire, but not required.</li>
<li>W2 employment preferred, but also open to C2C/1099.</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> </ul>
</p> </p>
@@ -44,41 +58,78 @@
Things you probably want to know: Things you probably want to know:
<ul> <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>Open to both contract and permanent positions.</li>
<li>I can start within a week.</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> </ul>
</p> </p>
<h2>Resume</h2> <h2>Resume</h2>
<p> <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> </p>
<h2>To send me job offers</h2> <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"> <form method="get" action="/email">
<label> <label>
<input type="checkbox" name="confirm_fully_remote_or_exceptional" value="yes" required> <input
<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> 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>
<label> <label>
<input type="checkbox" name="confirm_i_read_the_above" value="yes" required> <input
<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> 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>
<label> <label>
<input type="checkbox" name="confirm_include_pay" value="yes" required> <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>
<label> <label>
<input type="checkbox" name="confirm_no_ask_resume" value="yes" required> <input
<span>You will not ask for an updated resume. The latest resume is always available on this page.</span> 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>
<label> <label>
<div>Your email:</div> <div>Your email:</div>
@@ -87,6 +138,10 @@
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </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> </body>
</html> </html>

View File

@@ -57,16 +57,20 @@ in
repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; }; repo = mkOption { default = "${cfg.rsyncUser}@${cfg.rsyncHost}:borg-repos/liam-backup"; };
package = mkOption { default = pkgs.borgbackup; }; package = mkOption { default = pkgs.borgbackup; };
cmd = mkOption { default = lib.getExe cfg.package; }; cmd = mkOption { default = lib.getExe cfg.package; };
paths = mkOption { default = [ paths = mkOption {
default = [
"/var/lib/mail" "/var/lib/mail"
"/var/lib/dovecot" "/var/lib/dovecot"
"/var/log" "/var/log"
]; }; ];
};
keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; }; keyPath = mkOption { default = config.sops.secrets.liam-borg-key.path; };
}; };
config = { config = {
vacu.assertions = lib.singleton { 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"; message = "Only for version 1.4.x";
fatal = true; fatal = true;
}; };
@@ -88,7 +92,7 @@ in
group = cfg.user; group = cfg.user;
home = "/var/lib/auto-borg"; home = "/var/lib/auto-borg";
}; };
users.groups.${cfg.user} = {}; users.groups.${cfg.user} = { };
systemd.services.auto-borg-gen-key = { systemd.services.auto-borg-gen-key = {
script = '' script = ''
set -euo pipefail set -euo pipefail

View File

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

View File

@@ -31,6 +31,6 @@
vacu.packages = [ vacu.packages = [
"yt-dlp" "yt-dlp"
"radicle-node" "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 ]; imports = [ ./lean.nix ];
opts = { opts = {
@@ -23,7 +19,10 @@
lua_ls.enable = true; lua_ls.enable = true;
nixd = { nixd = {
enable = true; 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; pyright.enable = true;
rust_analyzer = { rust_analyzer = {

View File

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

View File

@@ -8,7 +8,7 @@
git, git,
libdbusmenu, libdbusmenu,
# options passed to the thunderbird builder, but with betterbird defaults # options passed to the thunderbird builder, but with betterbird defaults
privacySupport ? true, privacySupport ? true,
requireSigning ? false, requireSigning ? false,
allowAddonSideload ? true, allowAddonSideload ? true,
@@ -33,11 +33,14 @@ let
tag = version; tag = version;
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w="; hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
}; };
patchesFromThunderbird = runCommandNoCC "betterbird-patches-from-network" { patchesFromThunderbird =
runCommandNoCC "betterbird-patches-from-network"
{
outputHashAlgo = "sha256"; outputHashAlgo = "sha256";
outputHashMode = "recursive"; outputHashMode = "recursive";
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY="; outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
} '' }
''
set -xev set -xev
mkdir -p $out mkdir -p $out
fetchFromSeries() { fetchFromSeries() {
@@ -95,18 +98,18 @@ let
applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm applySeries ${betterbirdPatches}/${majorVersion}/series $out/comm
''; '';
in in
(thunderbird-128-unwrapped.override { (thunderbird-128-unwrapped.override { inherit privacySupport requireSigning allowAddonSideload; })
inherit privacySupport requireSigning allowAddonSideload; .overrideAttrs
}).overrideAttrs (old: rec { (old: rec {
pname = "betterbird"; pname = "betterbird";
inherit version; inherit version;
name = "${pname}-${version}"; name = "${pname}-${version}";
buildInputs = (old.buildInputs or []) ++ [ libdbusmenu ]; buildInputs = (old.buildInputs or [ ]) ++ [ libdbusmenu ];
src = replacement_src; src = replacement_src;
configureFlags = (old.configureFlags or []) ++ [ configureFlags = (old.configureFlags or [ ]) ++ [
# "--enable-application=comm/mail" # "--enable-application=comm/mail"
"--with-branding=comm/mail/branding/betterbird" "--with-branding=comm/mail/branding/betterbird"
# "--disable-updater" # "--disable-updater"
@@ -119,7 +122,9 @@ in
# "--enable-official-branding" # "--enable-official-branding"
]; ];
preConfigure = (old.preConfigure or "") + '' preConfigure =
(old.preConfigure or "")
+ ''
# Disable enforcing that add-ons are signed. # Disable enforcing that add-ons are signed.
export MOZ_REQUIRE_SIGNING= export MOZ_REQUIRE_SIGNING=
export MOZ_REQUIRE_ADDON_SIGNING=0 export MOZ_REQUIRE_ADDON_SIGNING=0
@@ -144,5 +149,13 @@ in
export MOZ_NO_PIE_COMPAT=1 export MOZ_NO_PIE_COMPAT=1
''; '';
passthru = (old.passthru or {}) // { inherit betterbirdPatches mozilla_src comm_src replacement_src patchesFromThunderbird; }; passthru = (old.passthru or { }) // {
}) inherit
betterbirdPatches
mozilla_src
comm_src
replacement_src
patchesFromThunderbird
;
};
})

View File

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

View File

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

View File

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

View File

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

View File

@@ -71,7 +71,8 @@ let
mkdir -p $out/liam 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 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 = { mailtestModule =
{
config, config,
pkgs, pkgs,
lib, lib,
@@ -82,7 +83,9 @@ let
imap-tools imap-tools
requests requests
]; ];
mkPkg = name: pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } '' mkPkg =
name:
pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
# flake8: noqa # flake8: noqa
${builtins.readFile ./mailtest/${name}.py} ${builtins.readFile ./mailtest/${name}.py}
''; '';
@@ -168,7 +171,12 @@ in
}; };
nodes.liam = nodes.liam =
{ lib, inputs, config, ... }: {
lib,
inputs,
config,
...
}:
{ {
imports = [ imports = [
"${inputs.self}/common" "${inputs.self}/common"
@@ -209,8 +217,10 @@ in
security.pki.certificateFiles = [ rootCA.certificatePath ]; security.pki.certificateFiles = [ rootCA.certificatePath ];
vacu.liam.backup.keyPath = pkgs.writeText "test-borg-key" testBorgKey; 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 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 ]; networking.hosts."${nodes.rsyncnet.networking.primaryIPAddress}" = [
config.vacu.liam.backup.rsyncHost
];
}; };
nodes.rsyncnet = nodes.rsyncnet =
@@ -218,7 +228,7 @@ in
let let
borgCfg = nodes.liam.vacu.liam.backup; borgCfg = nodes.liam.vacu.liam.backup;
user = borgCfg.rsyncUser; user = borgCfg.rsyncUser;
loginCommand = pkgs.writers.writeBashBin "special-rsync-login" {} '' loginCommand = pkgs.writers.writeBashBin "special-rsync-login" { } ''
set -euo pipefail set -euo pipefail
if [ -n "$SSH_ORIGINAL_COMMAND" ]; then if [ -n "$SSH_ORIGINAL_COMMAND" ]; then
echo "hi there" echo "hi there"
@@ -255,7 +265,7 @@ in
}; };
environment.systemPackages = [ environment.systemPackages = [
pkgs.borgbackup pkgs.borgbackup
(pkgs.writers.writeBashBin "borg-init" {} '' (pkgs.writers.writeBashBin "borg-init" { } ''
set -euo pipefail set -euo pipefail
export BORG_PASSPHRASE=${lib.escapeShellArg testBorgKey} export BORG_PASSPHRASE=${lib.escapeShellArg testBorgKey}
export BORG_NEW_PASSPHRASE=${lib.escapeShellArg testBorgKey} export BORG_NEW_PASSPHRASE=${lib.escapeShellArg testBorgKey}
@@ -268,9 +278,7 @@ in
{ pkgs, lib, ... }: { pkgs, lib, ... }:
{ {
imports = [ mailtestModule ]; imports = [ mailtestModule ];
environment.systemPackages = [ environment.systemPackages = [ pkgs.wget ];
pkgs.wget
];
networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress); networking.nameservers = lib.mkForce (lib.singleton nodes.ns.networking.primaryIPAddress);
}; };
@@ -288,7 +296,8 @@ in
}; };
dataJson = builtins.toJSON data; dataJson = builtins.toJSON data;
res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile; res = builtins.replaceStrings [ "@data@" ] [ dataJson ] rawFile;
in res; in
res;
skipTypeCheck = true; skipTypeCheck = true;
skipLint = true; skipLint = true;

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ liam.wait_for_unit("dovecot2.service")
relay.wait_for_unit("mailpit.service") relay.wait_for_unit("mailpit.service")
# generate and exchange keys so they can talk to eachother # 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") # 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 # # 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") # 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.wait_for_unit("auto-borg-gen-key.service")
liam_autoborger_key = liam.succeed("cat /var/lib/auto-borg/id_ed25519.pub").strip() 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("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") rsyncnet.succeed("sudo -u fm2382 borg-init")
liam.succeed("systemctl start auto-borg.service") liam.succeed("systemctl start auto-borg.service")
# liam.wait_for_unit("auto-borg.service") # liam.wait_for_unit("auto-borg.service")
@@ -51,21 +53,25 @@ class ImapMessage(TypedDict):
flags: list[str] flags: list[str]
body: str body: str
class ImapResult(TypedDict): class ImapResult(TypedDict):
result: bool result: bool
messages: list[ImapMessage] messages: list[ImapMessage]
def make_command(args: list) -> str: def make_command(args: list) -> str:
return " ".join(map(shlex.quote, (map(str, args)))) return " ".join(map(shlex.quote, (map(str, args))))
Arg = str|bool|int
Arg = str | bool | int
Args = dict[str, Arg] Args = dict[str, Arg]
def dict_args_to_list(dict_args: Args) -> list[str]: def dict_args_to_list(dict_args: Args) -> list[str]:
args:list[str] = [] args: list[str] = []
for k, v in dict_args.items(): for k, v in dict_args.items():
dashed = k.replace("_","-") dashed = k.replace("_", "-")
if isinstance(v, int) and not isinstance(v, bool): if isinstance(v, int) and not isinstance(v, bool):
v = str(v) v = str(v)
if isinstance(v, str) or (isinstance(v, bool) and v): if isinstance(v, str) or (isinstance(v, bool) and v):
@@ -74,12 +80,14 @@ def dict_args_to_list(dict_args: Args) -> list[str]:
args.append(v) args.append(v)
return args return args
class LogEntry(TypedDict): class LogEntry(TypedDict):
__CURSOR: str __CURSOR: str
_SYSTEMD_UNIT: str _SYSTEMD_UNIT: str
MESSAGE: str MESSAGE: str
pass pass
DEFAULT_JOURNALCTL_OPTS: Args = { DEFAULT_JOURNALCTL_OPTS: Args = {
"all": True, "all": True,
"output": "json", "output": "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. # 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} with_defaults = {**kwargs, **DEFAULT_JOURNALCTL_OPTS}
args = ["journalctl", *dict_args_to_list(with_defaults)] args = ["journalctl", *dict_args_to_list(with_defaults)]
res = machine.succeed(make_command(args)) res = machine.succeed(make_command(args))
for line in res.splitlines(): for line in res.splitlines():
data:LogEntry = json.loads(line) data: LogEntry = json.loads(line)
assert isinstance(data, dict) assert isinstance(data, dict)
for _, v in data.items(): for _, v in data.items():
assert isinstance(v, str) assert isinstance(v, str)
yield data yield data
class ProcessingWaiter(contextlib.AbstractContextManager): class ProcessingWaiter(contextlib.AbstractContextManager):
cursor: str = "" cursor: str = ""
timeout: int = 60 timeout: int = 60
_postfix_smtpd_connections: set[str] = set() _postfix_smtpd_connections: set[str] = set()
_postfix_queue: set[str] = set() _postfix_queue: set[str] = set()
def __init__(self, timeout=60): def __init__(self, timeout=60):
most_recent_entry = list(journalctl_log_entries(lines=1))[0] most_recent_entry = list(journalctl_log_entries(lines=1))[0]
self.cursor = most_recent_entry["__CURSOR"] self.cursor = most_recent_entry["__CURSOR"]
@@ -128,7 +141,9 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
assert sl_pid is not None assert sl_pid is not None
if message.startswith("connect from"): if message.startswith("connect from"):
self._postfix_smtpd_connections.add(sl_pid) 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) self._postfix_smtpd_connections.discard(sl_pid)
if unit == "postfix.service" and sl_ident == "postfix/qmgr": if unit == "postfix.service" and sl_ident == "postfix/qmgr":
queue_id = message.split(":")[0] queue_id = message.split(":")[0]
@@ -136,7 +151,10 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
self._postfix_queue.add(queue_id) self._postfix_queue.add(queue_id)
if message.endswith("removed"): if message.endswith("removed"):
self._postfix_queue.discard(queue_id) 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 return
else: else:
liam.sleep(1) liam.sleep(1)
@@ -144,32 +162,28 @@ class ProcessingWaiter(contextlib.AbstractContextManager):
def __exit__(self, _a, _b, _c): def __exit__(self, _a, _b, _c):
self.wait_until_finished() self.wait_until_finished()
class TesterThing():
class TesterThing:
uuid: str = "" uuid: str = ""
default_smtp: Args = {} default_smtp: Args = {}
default_imap: Args = {} default_imap: Args = {}
default_mailpit: 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.uuid = str(uuid.uuid4())
self.default_smtp = { self.default_smtp = {
"rcptto": "someone@example.com", "rcptto": "someone@example.com",
"host": f"{liam_ip}", "host": f"{liam_ip}",
**common, **common,
**smtp **smtp,
}
self.default_imap = {
"host": f"{liam_ip}",
**common,
**imap
}
self.default_mailpit = {
"mailpit-url": f"http://{relay_ip}:8025",
**mailpit
} }
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]: 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}") print(f"running {args!r}")
with ProcessingWaiter(): with ProcessingWaiter():
res = checker.succeed(make_command(args)) res = checker.succeed(make_command(args))
@@ -198,32 +212,34 @@ class TesterThing():
res_ty: ImapResult = res_unty # type: ignore res_ty: ImapResult = res_unty # type: ignore
return res_ty 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) res = self.run_imap(**kwargs)
assert res['result'] assert res["result"]
assert len(res['messages']) == 1 assert len(res["messages"]) == 1
message = res['messages'][0] message = res["messages"][0]
if mailbox is not None: if mailbox is not None:
assert message['folder'] == mailbox assert message["folder"] == mailbox
for flag in flags: for flag in flags:
assert flag in message['flags'] assert flag in message["flags"]
return self return self
def imap_found(self, **kwargs: Arg) -> 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: 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: def imap_move_to(self, dest: str, **kwargs: Arg) -> Self:
res = self.run_imap(move_to=dest, **kwargs) res = self.run_imap(move_to=dest, **kwargs)
assert res['result'] assert res["result"]
return self return self
def run_mailpit(self, **kwargs: Arg) -> bool: def run_mailpit(self, **kwargs: Arg) -> bool:
args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs} args = {"message_magic": self.uuid, **self.default_mailpit, **kwargs}
res = self.run_expecting_json("mailtest-mailpit", **args) res = self.run_expecting_json("mailtest-mailpit", **args)
return res['found_message'] return res["found_message"]
def mailpit_not_received(self, **kwargs: Arg) -> Self: def mailpit_not_received(self, **kwargs: Arg) -> Self:
received = self.run_mailpit(**kwargs) received = self.run_mailpit(**kwargs)
@@ -235,57 +251,78 @@ class TesterThing():
assert received assert received
return self return self
class Defaults():
class Defaults:
default_smtp: Args = {} default_smtp: Args = {}
default_imap: Args = {} default_imap: Args = {}
default_mailpit: Args = {} default_mailpit: Args = {}
def __init__(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool): def __init__(
self.default_smtp = { self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
**common, ):
**smtp self.default_smtp = {**common, **smtp}
} self.default_imap = {**common, **imap}
self.default_imap = { self.default_mailpit = {**mailpit}
**common,
**imap
}
self.default_mailpit = {
**mailpit
}
def make_tester(self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str|bool) -> TesterThing: def make_tester(
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
) -> TesterThing:
return TesterThing( return TesterThing(
smtp = {**self.default_smtp, **common, **smtp}, smtp={**self.default_smtp, **common, **smtp},
imap = {**self.default_imap, **common, **imap}, imap={**self.default_imap, **common, **imap},
mailpit = {**self.default_mailpit, **common, **mailpit}, 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 # 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 # 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("testFolder").imap_found_in("testFolder")
d.make_tester().smtp_accepted().imap_move_to("INBOX").imap_found_in("INBOX") d.make_tester().smtp_accepted().imap_move_to("INBOX").imap_found_in("INBOX")
# test the sieve script is working # test the sieve script is working
d.make_tester().smtp_accepted().imap_found_in("com.shelvacu") d.make_tester().smtp_accepted().imap_found_in("com.shelvacu")
# refilter doesnt activate on julie's # 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 = 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(
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"]) 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@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 # julie's emails should NOT get sieve'd like mine
d = Defaults(username="julie") 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="sales@theviolincase.com").imap_found()
d.make_tester().smtp_accepted(rcptto="superwow@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="bob@vacu.store")
TesterThing().smtp_rejected(mailfrom="shelvacu@shelvacu.com") TesterThing().smtp_rejected(mailfrom="shelvacu@shelvacu.com")
TesterThing().smtp_rejected(mailfrom="julie@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") TesterThing().smtp_rejected(mailfrom="reject-spam-test@example.com")
#people cant send as the wrong person # people cant send as the wrong person
d = Defaults(smtp = {"submission": True}) d = Defaults(smtp={"submission": True})
d.make_tester().smtp_rejected(mailfrom="julie@shelvacu.com", username="shelvacu") 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@theviolincase.com", username="shelvacu")
d.make_tester().smtp_rejected(mailfrom="fubar@vacu.store", username="julie") 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")
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")
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")
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? # 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") liam.succeed("systemctl start auto-borg.service")

View File

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

View File

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

View File

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