nix fmt
This commit is contained in:
@@ -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";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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,63 +125,62 @@ lib.mkMerge [
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
vacu.packages =
|
vacu.packages = with pkgs; [
|
||||||
with pkgs; [
|
bash
|
||||||
bash
|
bzip2
|
||||||
bzip2
|
curl
|
||||||
curl
|
ddrescue
|
||||||
ddrescue
|
diffutils
|
||||||
diffutils
|
dig
|
||||||
dig
|
dnsutils
|
||||||
dnsutils
|
ethtool
|
||||||
ethtool
|
file
|
||||||
file
|
findutils
|
||||||
findutils
|
gnugrep
|
||||||
gnugrep
|
gnused
|
||||||
gnused
|
gnutar
|
||||||
gnutar
|
gnutls
|
||||||
gnutls
|
gzip
|
||||||
gzip
|
hostname
|
||||||
hostname
|
htop
|
||||||
htop
|
inetutils
|
||||||
inetutils
|
iperf3
|
||||||
iperf3
|
iputils
|
||||||
iputils
|
jq
|
||||||
jq
|
killall
|
||||||
killall
|
lsof
|
||||||
lsof
|
mosh
|
||||||
mosh
|
nano
|
||||||
nano
|
ncdu
|
||||||
ncdu
|
netcat-openbsd
|
||||||
netcat-openbsd
|
nixos-rebuild
|
||||||
nixos-rebuild
|
openssh
|
||||||
openssh
|
"p7zip-unfree"
|
||||||
"p7zip-unfree"
|
pciutils
|
||||||
pciutils
|
progress
|
||||||
progress
|
psutils
|
||||||
psutils
|
pv
|
||||||
pv
|
ripgrep
|
||||||
ripgrep
|
rsync
|
||||||
rsync
|
screen
|
||||||
screen
|
# sed => gnused
|
||||||
# sed => gnused
|
sops
|
||||||
sops
|
sshfs
|
||||||
sshfs
|
ssh-to-age
|
||||||
ssh-to-age
|
# tar => gnutar
|
||||||
# tar => gnutar
|
tmux
|
||||||
tmux
|
tree
|
||||||
tree
|
tzdata
|
||||||
tzdata
|
# units => vacu-units
|
||||||
# units => vacu-units
|
unzip
|
||||||
unzip
|
usbutils
|
||||||
usbutils
|
util-linux
|
||||||
util-linux
|
"vacu-units"
|
||||||
"vacu-units"
|
vim
|
||||||
vim
|
wget
|
||||||
wget
|
which
|
||||||
which
|
xz
|
||||||
xz
|
zip
|
||||||
zip
|
];
|
||||||
];
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -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
|
||||||
|
@@ -1,8 +1,4 @@
|
|||||||
{
|
{ pkgs, inputs, ... }:
|
||||||
pkgs,
|
|
||||||
inputs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
inputs.jovian.nixosModules.jovian
|
inputs.jovian.nixosModules.jovian
|
||||||
|
55
flake.nix
55
flake.nix
@@ -175,10 +175,11 @@
|
|||||||
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 (inputs) self;
|
inherit nixpkgs;
|
||||||
};
|
inherit (inputs) self;
|
||||||
|
};
|
||||||
mkNixosConfig =
|
mkNixosConfig =
|
||||||
{
|
{
|
||||||
unstable ? false,
|
unstable ? false,
|
||||||
@@ -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" (
|
||||||
name = "liam";
|
import ./typesForTest.nix {
|
||||||
inherit (pkgs-stable) lib;
|
name = "liam";
|
||||||
inherit self;
|
inherit (pkgs-stable) lib;
|
||||||
inherit (inputs) nixpkgs;
|
inherit self;
|
||||||
});
|
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;
|
||||||
|
@@ -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 = ''
|
||||||
|
@@ -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>
|
||||||
|
@@ -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 {
|
||||||
"/var/lib/mail"
|
default = [
|
||||||
"/var/lib/dovecot"
|
"/var/lib/mail"
|
||||||
"/var/log"
|
"/var/lib/dovecot"
|
||||||
]; };
|
"/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;
|
||||||
};
|
};
|
||||||
@@ -75,7 +79,7 @@ in
|
|||||||
owner = cfg.user;
|
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+ = append to ACLs recursively
|
||||||
# "A+" = {
|
# "A+" = {
|
||||||
@@ -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
|
||||||
|
405
liam/sieve.nix
405
liam/sieve.nix
@@ -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,139 +146,155 @@ 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 =
|
||||||
let
|
attrs:
|
||||||
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
|
let
|
||||||
pairStrs = mapAttrsToList toPairStr attrs;
|
toPairStr = name: val: "${sieve_debug name} = ${sieve_debug val}; ";
|
||||||
pairsStr = concatStrings pairStrs;
|
pairStrs = mapAttrsToList toPairStr attrs;
|
||||||
in
|
pairsStr = concatStrings pairStrs;
|
||||||
"{ ${pairsStr}}"
|
in
|
||||||
;
|
"{ ${pairsStr}}";
|
||||||
sieve_debug = val:
|
sieve_debug =
|
||||||
if isString val then sieve_encode_string { allow_vars = true; for_debug_comment = true; with_quotes = true; } val
|
val:
|
||||||
else if (isInt val) || (isFloat val) then toString 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
|
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
|
||||||
assert isListWhere flags isString;
|
else
|
||||||
assert isListWhere conditions isString;
|
assert "dunno what to do with this";
|
||||||
assert (length flags) > 0;
|
null;
|
||||||
assert (length conditions) > 0;
|
pure_flags_impl =
|
||||||
let
|
flags: conditions:
|
||||||
argAttrs = { inherit flags conditions; };
|
assert isListWhere flags isString;
|
||||||
firstFlag = head flags;
|
assert isListWhere conditions isString;
|
||||||
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
|
assert (length flags) > 0;
|
||||||
in
|
assert (length conditions) > 0;
|
||||||
''
|
let
|
||||||
# pure_flags ${sieve_debug argAttrs};
|
argAttrs = { inherit flags conditions; };
|
||||||
removeflag ${sieve_quote_string firstFlag};
|
firstFlag = head flags;
|
||||||
if ${combined_condition} {
|
combined_condition = if (length conditions) == 1 then head conditions else (allof conditions);
|
||||||
${record_action "pure_flags ${concatStringsSep " " flags}"}
|
in
|
||||||
${concatStringsSep "\n" (map (flag: ''addflag ${sieve_quote_string flag};'') flags)}
|
''
|
||||||
}
|
# pure_flags ${sieve_debug argAttrs};
|
||||||
# pure_flags end
|
removeflag ${sieve_quote_string firstFlag};
|
||||||
'';
|
if ${combined_condition} {
|
||||||
pure_flags = flags: conditions:
|
${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 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 ${
|
||||||
(ihave "imapsieve")
|
allof [
|
||||||
(environment_matches "imap.user" "*")
|
(ihave "imapsieve")
|
||||||
(environment_matches "location" "MS")
|
(environment_matches "imap.user" "*")
|
||||||
(environment_matches "phase" "post")
|
(environment_matches "location" "MS")
|
||||||
]} {
|
(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
|
||||||
(envelope_is "amznbsns@shelvacu.com")
|
[ "amazon-ignore" "ignore" ]
|
||||||
(subject_matches [ "Your Amazon.com order has shipped*" "Your Amazon.com order of * has shipped!" ])
|
[
|
||||||
]}
|
(envelope_is "amznbsns@shelvacu.com")
|
||||||
${pure_flags ["bandcamp-ignore" "ignore"] [
|
(subject_matches [
|
||||||
(envelope_is "bandcamp@shelvacu.com")
|
"Your Amazon.com order has shipped*"
|
||||||
(subject_matches [ "* just announced a listening party on Bandcamp" "New items from *" "Starting in *" "New from *" ])
|
"Your Amazon.com order of * has shipped!"
|
||||||
]}
|
])
|
||||||
${pure_flags ["ika-ignore" "ignore"] (envelope_is "ika@dis8.net")}
|
]
|
||||||
${pure_flags ["ally-statement" "ignore"] [
|
}
|
||||||
(envelope_is "ally@shelvacu.com")
|
${pure_flags
|
||||||
(subject_is "Your latest statement is ready to view.")
|
[ "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 "bloomberg" (envelope_is "bloomberg@shelvacu.com")}
|
||||||
|
|
||||||
${pure_flags ["money-stuff" "not-spamish"] [
|
${pure_flags
|
||||||
(envelope_is "bloomberg@shelvacu.com")
|
[ "money-stuff" "not-spamish" ]
|
||||||
''header :matches "From" "\"Matt Levine\" *"''
|
[
|
||||||
]}
|
(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" "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 "tf2wiki@shelvacu.com")
|
envelope_is "lets-encrypt-mailing-list@shelvacu.com"
|
||||||
(from_is "noreply@wiki.teamfortress.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 "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" {
|
||||||
|
@@ -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
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -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 = {
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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,25 +33,28 @@ let
|
|||||||
tag = version;
|
tag = version;
|
||||||
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
|
hash = "sha256-9UG1juN/vKHY3LRuryjMDdaFapd6y7ySu0Fn3GTeN2w=";
|
||||||
};
|
};
|
||||||
patchesFromThunderbird = runCommandNoCC "betterbird-patches-from-network" {
|
patchesFromThunderbird =
|
||||||
outputHashAlgo = "sha256";
|
runCommandNoCC "betterbird-patches-from-network"
|
||||||
outputHashMode = "recursive";
|
{
|
||||||
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
|
outputHashAlgo = "sha256";
|
||||||
} ''
|
outputHashMode = "recursive";
|
||||||
set -xev
|
outputHash = "sha256-4OD7OckIA/qB0jI9dNk1Q6cTZZrKVufDNvPKSeEWYBY=";
|
||||||
mkdir -p $out
|
}
|
||||||
fetchFromSeries() {
|
''
|
||||||
local seriesFile="$1"
|
set -xev
|
||||||
shift
|
mkdir -p $out
|
||||||
filteredSeries="$(mktemp)"
|
fetchFromSeries() {
|
||||||
cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries"
|
local seriesFile="$1"
|
||||||
while IFS=" " read -r filename url; do
|
shift
|
||||||
${lib.getExe curl} -v -k "$url" --output "$out/$filename"
|
filteredSeries="$(mktemp)"
|
||||||
done < "$filteredSeries"
|
cat "$seriesFile" | grep " # " | grep -v "^#" | sed 's/ # / /' | sed 's:/rev/:/raw-rev/:' > "$filteredSeries"
|
||||||
}
|
while IFS=" " read -r filename url; do
|
||||||
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU}
|
${lib.getExe curl} -v -k "$url" --output "$out/$filename"
|
||||||
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series
|
done < "$filteredSeries"
|
||||||
'';
|
}
|
||||||
|
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series${stuff.MOZU}
|
||||||
|
fetchFromSeries ${betterbirdPatches}/${majorVersion}/series
|
||||||
|
'';
|
||||||
mozilla_src = fetchhg {
|
mozilla_src = fetchhg {
|
||||||
name = "mozilla--mozilla";
|
name = "mozilla--mozilla";
|
||||||
url = stuff.MOZILLA_REPO;
|
url = stuff.MOZILLA_REPO;
|
||||||
@@ -95,54 +98,64 @@ 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"
|
||||||
# "--disable-crashreporter"
|
# "--disable-crashreporter"
|
||||||
# "--enable-tests"
|
# "--enable-tests"
|
||||||
# "--without-wasm-sandboxed-libraries"
|
# "--without-wasm-sandboxed-libraries"
|
||||||
# "--with-unsigned-addon-scopes=app,system"
|
# "--with-unsigned-addon-scopes=app,system"
|
||||||
# "--allow-addon-sideload"
|
# "--allow-addon-sideload"
|
||||||
# "--enable-default-toolkit=cairo-gtk3-wayland"
|
# "--enable-default-toolkit=cairo-gtk3-wayland"
|
||||||
# "--enable-official-branding"
|
# "--enable-official-branding"
|
||||||
];
|
];
|
||||||
|
|
||||||
preConfigure = (old.preConfigure or "") + ''
|
preConfigure =
|
||||||
# Disable enforcing that add-ons are signed.
|
(old.preConfigure or "")
|
||||||
export MOZ_REQUIRE_SIGNING=
|
+ ''
|
||||||
export MOZ_REQUIRE_ADDON_SIGNING=0
|
# Disable enforcing that add-ons are signed.
|
||||||
|
export MOZ_REQUIRE_SIGNING=
|
||||||
# For NSS symbols
|
export MOZ_REQUIRE_ADDON_SIGNING=0
|
||||||
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; };
|
# 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
|
||||||
|
;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
|
# 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")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
urls="${new_urlsArray[*]}"
|
urls="${new_urlsArray[*]}"
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
self: _super: {
|
self: _super: {
|
||||||
fetchurlWithWetransfer = args: (self.fetchurl args).overrideAttrs (old: {
|
fetchurlWithWetransfer =
|
||||||
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [ ./convert-wetransfer-links.sh ];
|
args:
|
||||||
env.transferweeBin = self.lib.getExe self.transferwee;
|
(self.fetchurl args).overrideAttrs (old: {
|
||||||
env.NIX_DEBUG = "6";
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ ./convert-wetransfer-links.sh ];
|
||||||
});
|
env.transferweeBin = self.lib.getExe self.transferwee;
|
||||||
|
env.NIX_DEBUG = "6";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,13 @@
|
|||||||
{
|
{ lean4, fetchFromGitHub }:
|
||||||
lean4,
|
lean4.overrideAttrs (
|
||||||
fetchFromGitHub,
|
final: prev: {
|
||||||
}:
|
version = "4.19.0-rc3";
|
||||||
lean4.overrideAttrs (final: prev: {
|
src = fetchFromGitHub {
|
||||||
version = "4.19.0-rc3";
|
owner = "leanprover";
|
||||||
src = fetchFromGitHub {
|
repo = "lean4";
|
||||||
owner = "leanprover";
|
tag = "v4.19.0-rc3";
|
||||||
repo = "lean4";
|
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
|
||||||
tag = "v4.19.0-rc3";
|
};
|
||||||
hash = "sha256-3a+zMGr1JnjxCm9sx8ikTpPXUoaOxmO6o5I0akip+kU=";
|
cmakeFlags = (prev.cmakeFlags or [ ]) ++ [ "-DUSE_MIMALLOC=OFF" ];
|
||||||
};
|
}
|
||||||
cmakeFlags = (prev.cmakeFlags or []) ++ [ "-DUSE_MIMALLOC=OFF" ];
|
)
|
||||||
})
|
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
})
|
};
|
||||||
|
})
|
||||||
|
@@ -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]
|
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]
|
relay_creds: ENC[AES256_GCM,data:o0FIKyqYHo1mndY+TC6TopipDlZMoyePPPRF62+WVegWjnz+dG83WTzIduJ6qdzlkBH0tgYfau7aIzYaDWZAd935efxvwTMl8lot0xTa8SqAYxQKDkTcpUhaHtu9wlpaqv31vzPdGUJbI17e9ZPdMEPRNaEYQkYqP2YoagO17WRbzIOax+XTP08pyVJChDG++aYlkuScOXQyM830hDy2xCYA9OHN4BeyU5mh6W0BiXLYIp9oOh0y1We59CUKeo0S,iv:JHgLeQO6XE5VYsoPU4YrI+LIaWSETvfnnwjrlTc1n0g=,tag:cWafuECJy2Gv5BMGKG1NOw==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
|
- recipient: age1hkve3khk7fthyrwxjqdf4r37lrqpmnkz6mke7psuphvu2ykynqaq9g6ja5
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvNDFQQ3lMbGtBVjdBR0t2
|
||||||
MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi
|
MjJvcmpjQmtYR1VkSVYzTzJFZDZibmFKc1dNCkZpWUsvcEM2MnA2OWdOdXVsZzJi
|
||||||
VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6
|
VjFDOVNjdkVIZDgwWE5pQmpKWkxSb3MKLS0tIDlSbXZFY1R0dkl3NHdvSTlWYTZ6
|
||||||
bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P
|
bDV6UGVHd2RVKzVycHJUWllTMk1HU2MKkDag+K62PydC3jcvLaIxy0vOuANbA65P
|
||||||
hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA==
|
hzaTNzv8iotafjFDYLWim7PLnxv+IeywKoL+Pnn4o3+e0617omx1mA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
|
- recipient: age1dzdf4rgep3ctk3dnrmrqtdgrchaa8nszfc4dp29gqwsst3z6jyrq57vfsj
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnbWUrNTN0Y2lzSjR1ckc1
|
||||||
cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj
|
cW03WXZFVzBSUzdpUzVLMWJzRjhqaWRFODFBClJGSno0QUpQaGpVSzJ0Y3h5eXFj
|
||||||
aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD
|
aGpoNGIycG80NkxhWEFGeU9IMk1tWFEKLS0tIDI3Q3lHNGI1VWJBcFZDRDBqNGpD
|
||||||
RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO
|
RDFNajdSSWQ1ZWNNcXl0T3lLcm1YUWMKm7w5OXFeuk7Sby68ODrk9EC8SbvCTxoO
|
||||||
oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ==
|
oQueOepqeeh4wip3SQpHACvtUp4s85M6ZXE96uYioRlzy3zg39tIpQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2
|
- recipient: age1rz75dqzfd6gulwh270ukmt5amcau6j8dpxgzx8fm6u8sjkyx9usq69y4s2
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMUVLVWpHaksvZkJIb0U4
|
||||||
RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx
|
RnVTZ0k0L0VlMjFNNFg4RVZjTmk1OHEwbGpnCkIxTXN5aWMwTlZEWERYRXV5dHEx
|
||||||
UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx
|
UVFVVEczRFhWRDJPN3g0QVh2NXlZUjAKLS0tIGNRbkk3R1RYVCs2Y2x4UmZhTXdx
|
||||||
UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor
|
UVUrQStXTU9yUWJ0SnlIbDBIRUdSb00K9oPKVn1RzK0DVtaeXnfURea9k1lNzpor
|
||||||
3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg==
|
3ex6hSyrfzNazFlInCuptIFIpf5o1eeiiV2PL85w9wvpMh4MEG7peg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6
|
- recipient: age13x0f3glnz4jvqty2v92cxrrnjcna6ed4qegrhulw9jjy08zuy3aqzvrfc6
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1MHUxcU9tR3JKSjk5TGRm
|
||||||
c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs
|
c2I3S0lrV1RJZHFkN1JyNHlqc2hXbGtPVlVzCk9pMmVRdC92bld6SW0rNFVyRmJs
|
||||||
QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI
|
QmVOMXRrb3FvVUNUYnVuczg5MklEL1kKLS0tIEE2YkRmeWFONVpDTk02S3kwSWNI
|
||||||
Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw
|
Ty9PdGYxUnRNSUIxN21RWWJUQnVqWjAKp1KybOk5/5xHHggBwE7zyuOw17GwxPCw
|
||||||
UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA==
|
UR2R5wuc0d1Uyb/z/QvRI4lbpjAhjb749JgLE2IYTYLfPsJv59K8BA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d
|
- recipient: age13j6l33g0ghk4vezn0qwfal2qmcgqwkv89ejwezpe3n47mw8yxyuslj6y7d
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2c2cyOWh3bEtBMUF6a3hx
|
||||||
UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK
|
UjVIYkN5cE1ZcWNZM1V3Y3lhR05JYUt4Q3djCk9XNWF1dnhveVlLNWxJSVcxcVRK
|
||||||
V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0
|
V2d2aWx5ZXdrYUw0TFN3VGVZTE5RTTAKLS0tIDNnWm5nbDZUbmh3QTBCWXp6aUE0
|
||||||
ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX
|
ZFhoeXRTOEhDT2NpOXM2L3NCdVNEQmMKBp4e23mcqrJdlcqbf6mUjitYq7MxkeoX
|
||||||
jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A==
|
jX8LQTucw9dhLu/SCxymRxg9/Q2+PfhUvDR2L51tdlbr77dRhic3/A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57
|
- recipient: age1vla9w33lsp03s46p9p6gc2mvr844vthdqhc2hzau2ph6h60gmyqqh9sf57
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoeWltSkV6aGJ1WkJOVTBp
|
||||||
Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3
|
Q24wMEFuWlVQYXMrKzRrSHN2THB3TWtYQ0VvCldHUmlpUGdNTlp4QkluZjRzK0J3
|
||||||
U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM
|
U0ZGYWM2eFZyZHhuT2dWSnBJdzA0dmMKLS0tIHg0citENmY1QkpXNURzY2x4QkZM
|
||||||
bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp
|
bG9DUTFkd2t3YXFXVElKK3JsK216Rm8KGvXixYViOUwrVarBMZeUI5HlCBtoL5bp
|
||||||
7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg==
|
7uZ9JFKQMh9EtiUk+Pr2xr4r9Mah0Gk3AmmVKWvaQaC/bkEIhe30Eg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl
|
- recipient: age1jy8mxcndkw6zd6q99tjgz3gsynn78x2lwtrff85u6ud9g9y9z5mspvhufl
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnYmhVb0FsdUc5RjdPWnA5
|
||||||
ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz
|
ZmpaMi9Rek5WM1AvSVl3Nk1maG1YanJaS0RzCjM3VEJKM3dVclZxK2FSMENKTUUz
|
||||||
d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0
|
d0dleUU2Rk5namdUdFl4ZjNSM05xdnMKLS0tIHRzYldRM0I4MytMcGFMUnZ3QXA0
|
||||||
MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw
|
MGtKcDMyejNFNktCL2I4RUI3Qkk1TWMKsxjqBw5J91f3T9TDHNAKFI2cTT4i7zJw
|
||||||
N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw==
|
N33KbrskOaOXjCsoENnqdRl9Y7v/JbOh5YQ2/oPwZEfuwgHG9lcXqw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp
|
- recipient: age148huz6rc3q9xx5t873ncx75sja2sazlescwspxl7lsmxsqkz0apsy8cldp
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdWRCdnNybjdzSFpacjNj
|
||||||
eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ
|
eFNJNjBzYmpsRkw4czN2aWJzSnBDeFYweDM4CmZCcnZCTEJQTGtoSlo3VW45T0ZJ
|
||||||
bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr
|
bmpUMHhFMy9mSUxaTWVCcFBnQlAramsKLS0tIGV3eHcxRlJZc3BxQUU3TUhsRVAr
|
||||||
VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a
|
VXdheGpVRFF2UFBKQTF0OFMrVzdYcjQKaEs1irVwO0OoXbBhYd1AgCCPPF3sFH3a
|
||||||
go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg==
|
go3jAHOCnwkYQMVRd24FGZx28XuEgeXQALk7JqEEy5eCS6nKDEVqcg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s
|
- recipient: age197a33mlf5294amjx59hycctu6wm4l3cu3w7n9rv3fs9340ql64rqjzpr7s
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHcWZxa3NHR0Z4TmlNNHVU
|
||||||
aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5
|
aFNvN2tycVd2THhFMGtMckhGOXBuZXNMSkFFCm1VR1ZwUHdabFdBWmUxUXVxTVR5
|
||||||
eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ
|
eFVvakFDZUV2WHByU2pRU3hrWXVaMGcKLS0tIHRjbElYOU8xaW1lVFlrL0YwMDlQ
|
||||||
MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q
|
MEwvd1RQd1hlNVNZL3VveUkydVNjVE0KFsyjr38WdXu4R0038Dum0VeVw+LNcI6q
|
||||||
4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ==
|
4R0ft0KsfLLmPgoNIdK5Dq5hUxyGVe8Ej/9KaN0UrqIRsLHCHimYyQ==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru
|
- recipient: age1sqj8z3feqm2dk3gj8mxpfn5dpqnsmus862e8ayd0d4cdresqffdswcf9ru
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4QW9yVk9zN2RrZkpTWXZU
|
||||||
U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t
|
U080M1pDdzV1bDFSR2UrY0o2dnoyYlpNZXc0CmJCSE84L1ZRdUVZc21GbWc3cG9t
|
||||||
NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2
|
NHRGQUFVS3U1TjFVYWl1Q1FyODY3UjgKLS0tIGhrY1dMa251R1hCc0F5eDhtWnc2
|
||||||
bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq
|
bXpqNkVobzgwMHJIdHBFZ0xDZ2RzcmcK0m4awMUrdwYvXO14L1hvhcaGgLOW3FCq
|
||||||
UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig==
|
UU1Vc/vX32Lsu1BN4aXlTZ1jHD6R6CnV5TbUTcM/jxFRKoRzDwdJig==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-04-16T00:39:52Z"
|
lastmodified: "2025-04-16T00:39:52Z"
|
||||||
mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str]
|
mac: ENC[AES256_GCM,data:5rVkhMPb+gpB1fCCvRbillDO4MXl6TbKQar+hjRqy6bYjFejZgaAtAObbGL0n05/1XKpc3ULUXq7kQEOyjic6qhLx+o6biwkJFEjoF+0CePtTS9zRHXYDK1hnpgqMzVlVUH6KHipDJL9DAnDoJks3wc7gQaj4WjoGSlydDIcFPA=,iv:4JD9s2zkZjMoWRut9aawQjRhPo3q4FZrzoSTB6qyWgY=,tag:ZLzlb9TSP1agyPbWtP/mkA==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.10.1
|
version: 3.10.1
|
||||||
|
@@ -71,31 +71,34 @@ 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,
|
{
|
||||||
pkgs,
|
config,
|
||||||
lib,
|
pkgs,
|
||||||
...
|
lib,
|
||||||
}:
|
...
|
||||||
let
|
}:
|
||||||
libraries = with pkgs.python3Packages; [
|
let
|
||||||
imap-tools
|
libraries = with pkgs.python3Packages; [
|
||||||
requests
|
imap-tools
|
||||||
];
|
requests
|
||||||
mkPkg = name: pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
|
];
|
||||||
# flake8: noqa
|
mkPkg =
|
||||||
${builtins.readFile ./mailtest/${name}.py}
|
name:
|
||||||
'';
|
pkgs.writers.writePython3Bin "mailtest-${name}" { inherit libraries; } ''
|
||||||
in
|
# flake8: noqa
|
||||||
{
|
${builtins.readFile ./mailtest/${name}.py}
|
||||||
options.vacu.mailtest = lib.mkOption { readOnly = true; };
|
'';
|
||||||
config.vacu.mailtest = {
|
in
|
||||||
smtp = mkPkg "smtp";
|
{
|
||||||
imap = mkPkg "imap";
|
options.vacu.mailtest = lib.mkOption { readOnly = true; };
|
||||||
mailpit = mkPkg "mailpit";
|
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
|
in
|
||||||
{
|
{
|
||||||
name = "liam-receives-mail";
|
name = "liam-receives-mail";
|
||||||
@@ -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,27 +278,26 @@ 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);
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript =
|
testScript =
|
||||||
let
|
let
|
||||||
rawFile = builtins.readFile ./testScript/main.py;
|
rawFile = builtins.readFile ./testScript/main.py;
|
||||||
data = {
|
data = {
|
||||||
checkSieve = lib.getExe nodes.liam.vacu.checkSieve;
|
checkSieve = lib.getExe nodes.liam.vacu.checkSieve;
|
||||||
acmeTest = pkgs.writeText "acme-test" "test";
|
acmeTest = pkgs.writeText "acme-test" "test";
|
||||||
acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test";
|
acmeTestDest = nodes.liam.security.acme.defaults.webroot + "/.well-known/acme-challenge/test";
|
||||||
relayIP = nodes.relay.networking.primaryIPAddress;
|
relayIP = nodes.relay.networking.primaryIPAddress;
|
||||||
liamIP = nodes.liam.networking.primaryIPAddress;
|
liamIP = nodes.liam.networking.primaryIPAddress;
|
||||||
inherit testBorgKey;
|
inherit testBorgKey;
|
||||||
borgExe = lib.getExe pkgs.borgbackup;
|
borgExe = lib.getExe pkgs.borgbackup;
|
||||||
};
|
};
|
||||||
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;
|
||||||
|
@@ -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
|
||||||
@@ -87,7 +92,7 @@ try:
|
|||||||
assert uid is not None
|
assert uid is not None
|
||||||
info(f"about to move {uid} to {args.move_to}")
|
info(f"about to move {uid} to {args.move_to}")
|
||||||
res = mailbox.move(uid, 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}")
|
info(f"done moving, res {res!r}")
|
||||||
|
|
||||||
with connection() as mailbox:
|
with connection() as mailbox:
|
||||||
@@ -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],
|
||||||
)
|
)
|
||||||
|
@@ -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)
|
||||||
|
@@ -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)
|
||||||
|
@@ -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))
|
||||||
|
@@ -6,7 +6,7 @@ DATA_JSON = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from hints import * # type: ignore
|
from hints import * # type: ignore
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import shlex
|
import shlex
|
||||||
@@ -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,15 +80,17 @@ 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",
|
||||||
}
|
}
|
||||||
|
|
||||||
# --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.
|
# 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))
|
||||||
@@ -195,35 +209,37 @@ class TesterThing():
|
|||||||
def run_imap(self, **kwargs: Arg) -> ImapResult:
|
def run_imap(self, **kwargs: Arg) -> ImapResult:
|
||||||
args = {"message_magic": self.uuid, **self.default_imap, **kwargs}
|
args = {"message_magic": self.uuid, **self.default_imap, **kwargs}
|
||||||
res_unty = self.run_expecting_json("mailtest-imap", **args)
|
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
|
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
|
def make_tester(
|
||||||
}
|
self, smtp: Args = {}, imap: Args = {}, mailpit: Args = {}, **common: str | bool
|
||||||
self.default_mailpit = {
|
) -> TesterThing:
|
||||||
**mailpit
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
{
|
{ ... }:
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
{
|
||||||
# systemd.services.dovecot-backup-sftp-server = {
|
# systemd.services.dovecot-backup-sftp-server = {
|
||||||
# enable = true;
|
# enable = true;
|
||||||
#
|
#
|
||||||
# };
|
# };
|
||||||
}
|
}
|
||||||
|
@@ -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" ];
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user