From ca5881d12c81ef209346fa7f88c5811c598d7627 Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 22:48:48 +0200 Subject: [PATCH 01/12] add webdav service --- hosts/dav/default.nix | 37 ++++++++++++ hosts/dav/radicale.nix | 105 ++++++++++++++++++++++++++++++++++ secrets/radicale_htpasswd.age | Bin 0 -> 941 bytes secrets/secrets.nix | 2 + 4 files changed, 144 insertions(+) create mode 100644 hosts/dav/default.nix create mode 100644 hosts/dav/radicale.nix create mode 100644 secrets/radicale_htpasswd.age diff --git a/hosts/dav/default.nix b/hosts/dav/default.nix new file mode 100644 index 0000000..d76a35c --- /dev/null +++ b/hosts/dav/default.nix @@ -0,0 +1,37 @@ +{ ... }: + +{ + imports = [ + ../common.nix + ../../services/openssh.nix + ../../services/prometheus-node.nix + ./radicale.nix + ]; + + networking = { + hostName = "dav"; + firewall = { + allowedTCPPorts = [ + 80 # HTTP/1 + 443 # HTTP/2 + ]; + allowedUDPPorts = [ + 443 # HTTP/3 + ]; + }; + }; + + services = { + openssh.banner = '' + __ + /\ \ + \_\ \ __ __ __ + /'_` \ /'__`\ /\ \/\ \ + /\ \L\ \/\ \L\.\_\ \ \_/ | + \ \___,_\ \__/.\_\\ \___/ + \/__,_ /\/__/\/_/ \/__/ + ''; + }; + + system.stateVersion = "26.05"; +} diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix new file mode 100644 index 0000000..c5ec70b --- /dev/null +++ b/hosts/dav/radicale.nix @@ -0,0 +1,105 @@ +{ config, pkgs, ... }: + +let + calendarAggregate = pkgs.writers.writePython3 "calendar-aggregate.py" { libraries = [ pkgs.python3Packages.icalendar ]; } '' + from pathlib import Path + from icalendar import Calendar + + combined = Calendar() + + for path in Path("/var/lib/radicale/collections").rglob("*.ics"): + with open(path, "rb") as f: + cal = Calendar.from_ical(f.read()) + + for component in cal.walk("VEVENT"): + combined.add_component(component) + + with open("/srv/www/calendar/all.ics", "wb") as f: + f.write(combined.to_ical()) + ''; +in +{ + imports = [ + ../../services/nginx.nix + ../../services/prometheus-nginx.nix + ]; + + systemd = { + services.calendar-aggregate = { + script = "${calendarAggregate}"; + serviceConfig.Type = "oneshot"; + }; + timers.calendar-aggregate = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "5m"; + OnUnitActiveSec = "5m"; + }; + }; + }; + + services = { + radicale = { + enable = true; + rights = { + readonly = { + user = ".*"; + collection = ".*"; + permissions = "r"; + }; + cccb = { + user = "cccb"; + collection = "cccb"; + permissions = "rw"; + }; + openwrt = { + user = "openwrt"; + collection = "openwrt"; + permissions = "rw"; + }; + }; + settings = { + server = { + hosts = [ "[::1]:5232" ]; + validate_user_value = "strict"; + validate_path_value = "strict"; + }; + auth = { + type = "htpasswd"; + htpasswd_filename = config.age.secrets.radicale_htpasswd.path; + htpasswd_encryption = "bcrypt"; + }; + storage.filesystem_folder = "/var/lib/radicale/collections"; + headers."Access-Control-Allow-Origin" = "*"; + }; + }; + + nginx.virtualHosts."dav.${config.networking.domain}" = { + default = true; + quic = true; + kTLS = true; + forceSSL = true; + enableACME = true; + locations = { + "/" = { + proxyPass = "http://[::1]:5232"; + recommendedProxySettings = true; + extraConfig = '' + proxy_pass_header Authorization; + ''; + }; + "/all.ics".root = "/srv/www/calendar"; + "/status" = { + proxyPass = "http://${cfg.host}:${toString cfg.port}"; + recommendedProxySettings = true; + extraConfig = '' + allow 195.160.173.14; + allow 2001:678:760:cccb::14; + deny all; + ''; + }; + }; + }; + }; +} + diff --git a/secrets/radicale_htpasswd.age b/secrets/radicale_htpasswd.age new file mode 100644 index 0000000000000000000000000000000000000000..f967a4f6c7a10c49101ce7e09500354c16789378 GIT binary patch literal 941 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH_0Y~UELRB5i7-mb zOb!k3D9LuOEOQU?Pj;>H4J=6YHpnwDDa`RJNy-Q=a11SQOy+V-&8`TrG)Ss+vn(`m z&Q2{$D$GqPG&VF!&UH`p3U_jL&kQsR@yIUlwm`Sdwah0eyj;P*G$*qx-=NsZJUPP2 zB)K%o&rH8O#3jHdEiK#J*+V}$GdM9Q+s7v;*MKW9(lXS$EUP%lF(lpD%g@o$Bdave zG1J>Q#N8>+)y&v0+s7n4(b3I4!xi1O6kqK$pFo8Ilf(em@YE_}@8s+pr*OZ#;v!cc zgY5F603W}s{K~4J@tN-Rsf+`{$UOM<;JoN`JtoqTgmyo)?tD@>h2%uAEZ z3@waGDzedS)30<6bxv3CaZmK~iK;X-%gZcvi7XAVjLOVT$&RRU&oyx?)sM`rN-}V^ z&@VJ{jY#K;Dm3y6^-uH2NHWbd@~F%-PAf8u$}0}h4l=7s&hf2GNlz;b@Nspqw7`fr zH}?Wl!*qqH;Os<`A|nF>ud+~|+=Artz?`zsB-ikg00RTlwBU68^kC=AJd;8Lmq4x{ zcQent0)JD#M3bc4GHnA>gp;aRXAmMSq2$d!mdPN)DPcHo_a&cXY-`_O>+iD-CC2L8pS|7}5K4l_j mFMnCo_Qj6n?02pmPkFSuZMQd Date: Thu, 18 Jun 2026 22:56:15 +0200 Subject: [PATCH 02/12] add dav --- flake.nix | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/flake.nix b/flake.nix index f805528..aa3254b 100644 --- a/flake.nix +++ b/flake.nix @@ -197,6 +197,26 @@ ./hosts/sql ]; }; + nixosConfigurations."dav" = nixpkgs.lib.nixosSystem { + #system = "x86_64-linux"; + #pkgs = import nixpkgs { inherit system; }; + inherit system; + modules = [ + agenix.nixosModules.default + { environment.systemPackages = [ (agenix.packages.${system}.default) ]; } + { + age.secrets = { + radicale-htpasswd = { + file = ./secrets/radicale-htpasswd.age; + owner = "radicale"; + group = "radicale"; + mode = "0400"; + }; + }; + } + ./hosts/dav + ]; + }; }; #); } From 35c5877e7c3b84dd97798772421b0ee22b18395f Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 22:59:01 +0200 Subject: [PATCH 03/12] cleanup --- hosts/dav/radicale.nix | 9 --------- 1 file changed, 9 deletions(-) diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index c5ec70b..be4700f 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -89,15 +89,6 @@ in ''; }; "/all.ics".root = "/srv/www/calendar"; - "/status" = { - proxyPass = "http://${cfg.host}:${toString cfg.port}"; - recommendedProxySettings = true; - extraConfig = '' - allow 195.160.173.14; - allow 2001:678:760:cccb::14; - deny all; - ''; - }; }; }; }; From 61f3a286018127b32baf4638bdaf336ccde4c354 Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 22:59:42 +0200 Subject: [PATCH 04/12] cleanup --- hosts/dav/radicale.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index be4700f..27f46a0 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -66,7 +66,7 @@ in }; auth = { type = "htpasswd"; - htpasswd_filename = config.age.secrets.radicale_htpasswd.path; + htpasswd_filename = config.age.secrets.radicale-htpasswd.path; htpasswd_encryption = "bcrypt"; }; storage.filesystem_folder = "/var/lib/radicale/collections"; From 0be9b6f73c6031f18b4071696df0971570b9b34b Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 23:01:34 +0200 Subject: [PATCH 05/12] fix sshd --- hosts/dav/default.nix | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/hosts/dav/default.nix b/hosts/dav/default.nix index d76a35c..3009519 100644 --- a/hosts/dav/default.nix +++ b/hosts/dav/default.nix @@ -21,17 +21,16 @@ }; }; - services = { - openssh.banner = '' - __ - /\ \ - \_\ \ __ __ __ - /'_` \ /'__`\ /\ \/\ \ - /\ \L\ \/\ \L\.\_\ \ \_/ | - \ \___,_\ \__/.\_\\ \___/ - \/__,_ /\/__/\/_/ \/__/ - ''; - }; + environment.etc."ssh/banner".text = '' + __ + /\ \ + \_\ \ __ __ __ + /'_` \ /'__`\ /\ \/\ \ + /\ \L\ \/\ \L\.\_\ \ \_/ | + \ \___,_\ \__/.\_\\ \___/ + \/__,_ /\/__/\/_/ \/__/ + ''; + services.openssh.settings.Banner = "/etc/ssh/banner"; system.stateVersion = "26.05"; } From 126dac17650ec083c70e018facfdd64d9d80464f Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Thu, 18 Jun 2026 23:03:24 +0200 Subject: [PATCH 06/12] fix flake --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index aa3254b..89049f9 100644 --- a/flake.nix +++ b/flake.nix @@ -207,7 +207,7 @@ { age.secrets = { radicale-htpasswd = { - file = ./secrets/radicale-htpasswd.age; + file = ./secrets/radicale_htpasswd.age; owner = "radicale"; group = "radicale"; mode = "0400"; From cbb37ffd1f44161d997f0044874e3a9fd2c74dab Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Fri, 19 Jun 2026 20:53:12 +0200 Subject: [PATCH 07/12] add cccb user --- hosts/dav/radicale.nix | 2 +- secrets/radicale_htpasswd.age | Bin 941 -> 1007 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index 27f46a0..29a8bb5 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -49,7 +49,7 @@ in }; cccb = { user = "cccb"; - collection = "cccb"; + collection = ".*"; permissions = "rw"; }; openwrt = { diff --git a/secrets/radicale_htpasswd.age b/secrets/radicale_htpasswd.age index f967a4f6c7a10c49101ce7e09500354c16789378..64ac55d7d508c553459a12080b67d590a736315f 100644 GIT binary patch delta 920 zcmZ3>{+@k;PJN>Teu;lTR;6iye@bd`RZ+2LetB|U zHdldLeq@fBSz3l+xUaESMnpwYR&r@^dYETaNq9+Wv1Lh=dAYBfuYXi#GMBEMLUD11 zZfc5=si~o*f@_&iQh2#SuupEVL2*e$X{e>CZ?I2Lx?81dfp%EBi;<;vp+!b+y0@i? zbF#Z(zDIUCS44hFflG04g++*^M{;6HR+fcNadv32xnXjVr%_pEgk?@xUZQ(`Xik#B z#E;_PMdhw}*%=k-ZmEGm+9oE>&Vd$=;qG4E>4sU}`X*)WrNLDx=FXKqh3GTl76G94qmO|!%ELqbaf%gZyu zl2aoLO~PD5vOP?_9F5HM!wL*7T=R3o-A#NapJf!UkIeRV3N23aORUHb4J*kuvM@+8 z^E7fw_f0eOjLgn+^>NbntxU^Hj>>W5a`Ff(%P9?tN-9pu3-IzQiwLX8k94arb27|v zvJ7)gbI!>0@%1dta0@g+j{!IL0#n0ug~EskZGRuX@CplWvs4pL|A6|`&|s&s5XTUY zydn$3Wb=$j13wpM=Nxy(bT02~QzuWqtR$ZxSAT;-4|Ai8$l}V<2&3HKGGp^9$B1N; zY`?UO2>tXdUv%r7JPIte%N4S7Qxgr+jJ)0S4NRh3vfY!CicKvn9Sw{OvjYsWDt*iJ z>nrn21I#P3odUVsT)e}x+$%Hmz05q*J&l73jB-P&Of1Wb{1WrSe4UH}G7Cc-eG3CD z5`DRJb#)cOGYrd%io!FTj7yRXjNN^6gGzlo!V0R)5>tvx+zK+B9P>@9LJM;XOY*t? zF(_sz=wGN^=^n&;y)Smfr*|b1Hnnf>8r9Ff&L5>=mmngh$nab0W&6>^FP6Uw->iT5 zjlSyQ_J{(@;LfAfIKCG4N3zCY_BkQbvbjg}qpP#F!{B6A$l*#>c&AA0C b1)-f(z5iGQyL?$_)$yligDCQvyu9k_}QaGc5D<^&RuM^tH3AB1$YvyxhX|-AjVK zGMsWsGo5^MO}vXdT`NqTLd;8(%nU7zN-DA^pJf!U_i<13^NFf7G|S5@b%`tuv5d;h zPRWj_a?dq!E7gz8tx7U*w$Lv$a*asmiYhen3iVI($Vf8HH1ep-G)^lrjLItx(GD`J zO3v}EOi52G4DfMvv9!R50XO#oQ^RzHsNn2GlOiJn1Fy1BpWK4v^7_D>vd|>g@R9%n z1JktNbp7;T=gd5lLIamTt{`_a&%6SEQ@=!$q}(!X15@LooB*$s(&BQ{lI%3c07G~8 zz~Z!Ue{Jnh^aywID6rHnS1`#n3b#n|N_Wo5bMiAX&Wh0XsmjRl4@(Sl_ROxV%&OF` z&oW51i1JJiv*2z#c z-=E&v-;udj>|nWlK<@T9HC`Wvsq4!Y_&5ZGv0mR8)jVazysUSB^olmPpIrJ= Date: Fri, 19 Jun 2026 23:33:43 +0200 Subject: [PATCH 08/12] fix rights --- hosts/dav/radicale.nix | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index 29a8bb5..8b0e8b5 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -42,19 +42,19 @@ in radicale = { enable = true; rights = { - readonly = { + root = { user = ".*"; - collection = ".*"; + collection = ""; permissions = "r"; }; - cccb = { - user = "cccb"; - collection = ".*"; + calendars = { + user = ".*"; + collection = "{user}/[^/]+"; permissions = "rw"; }; - openwrt = { - user = "openwrt"; - collection = "openwrt"; + principal = { + user = ".*"; + collection = "{user}"; permissions = "rw"; }; }; From 6c5bbd557612c25b38eaa74f56d9a5c002c3bfba Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Fri, 19 Jun 2026 23:46:01 +0200 Subject: [PATCH 09/12] fix rights --- hosts/dav/radicale.nix | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index 8b0e8b5..b8a2873 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -47,14 +47,14 @@ in collection = ""; permissions = "r"; }; - calendars = { - user = ".*"; - collection = "{user}/[^/]+"; - permissions = "rw"; - }; principal = { user = ".*"; - collection = "{user}"; + collection = "{user}/?"; + permissions = "rw"; + }; + collections = { + user = ".*"; + collection = "{user}/.*"; permissions = "rw"; }; }; From 96d15b02fe01064f0faa5be6cf8fceb9a57fac61 Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Sat, 20 Jun 2026 00:27:07 +0200 Subject: [PATCH 10/12] switch to baikal --- hosts/dav/baikal.nix | 24 ++++++++++++++++++++++++ hosts/dav/default.nix | 3 ++- hosts/dav/radicale.nix | 8 ++++---- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 hosts/dav/baikal.nix diff --git a/hosts/dav/baikal.nix b/hosts/dav/baikal.nix new file mode 100644 index 0000000..8b7c5c1 --- /dev/null +++ b/hosts/dav/baikal.nix @@ -0,0 +1,24 @@ +{ ... }: + +{ + imports = [ + ../../services/nginx.nix + ]; + + services = { + baikal = { + enable = true; + virtualHost = "dav.${config.networking.domain}"; + }; + + nginx.virtualHosts."dav.${config.networking.domain}" = { + default = true; + quic = true; + kTLS = true; + forceSSL = true; + enableACME = true; + locations."/all.ics".root = "/srv/www/calendar"; + }; + }; +} + diff --git a/hosts/dav/default.nix b/hosts/dav/default.nix index 3009519..a7e857f 100644 --- a/hosts/dav/default.nix +++ b/hosts/dav/default.nix @@ -5,7 +5,8 @@ ../common.nix ../../services/openssh.nix ../../services/prometheus-node.nix - ./radicale.nix + #./radicale.nix + ./baikal.nix ]; networking = { diff --git a/hosts/dav/radicale.nix b/hosts/dav/radicale.nix index b8a2873..47b120b 100644 --- a/hosts/dav/radicale.nix +++ b/hosts/dav/radicale.nix @@ -42,14 +42,14 @@ in radicale = { enable = true; rights = { - root = { - user = ".*"; - collection = ""; + readonly = { + user = ""; + collection = ".*"; permissions = "r"; }; principal = { user = ".*"; - collection = "{user}/?"; + collection = "{user}"; permissions = "rw"; }; collections = { From 323c58ceddfd8cfc331f3c6d9f04cf07928db1fd Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Sat, 20 Jun 2026 00:29:33 +0200 Subject: [PATCH 11/12] fix module --- hosts/dav/baikal.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/dav/baikal.nix b/hosts/dav/baikal.nix index 8b7c5c1..91c2a15 100644 --- a/hosts/dav/baikal.nix +++ b/hosts/dav/baikal.nix @@ -1,4 +1,4 @@ -{ ... }: +{ config, ... }: { imports = [ From f9952abd5431f3f0af2711f16dce414bd7613888 Mon Sep 17 00:00:00 2001 From: "Ricardo (XenGi) Band" Date: Sat, 20 Jun 2026 00:42:12 +0200 Subject: [PATCH 12/12] add baikal to postgres --- flake.nix | 6 ++++++ hosts/sql/postgres.nix | 1 + secrets/postgres-baikal.age | Bin 0 -> 1047 bytes secrets/secrets.nix | 9 ++++----- 4 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 secrets/postgres-baikal.age diff --git a/flake.nix b/flake.nix index 89049f9..f2f73ff 100644 --- a/flake.nix +++ b/flake.nix @@ -192,6 +192,12 @@ group = "postgres"; mode = "0400"; }; + postgres-baikal = { + file = ./secrets/postgres-baikal.age; + owner = "postgres"; + group = "postgres"; + mode = "0400"; + }; }; } ./hosts/sql diff --git a/hosts/sql/postgres.nix b/hosts/sql/postgres.nix index cd1fa90..fb46209 100644 --- a/hosts/sql/postgres.nix +++ b/hosts/sql/postgres.nix @@ -14,6 +14,7 @@ let (mkEntry "hedgedoc" 26) # md.berlin.ccc.de (mkEntry "grafana" 14) # monitoring.berlin.ccc.de (mkEntry "forgejo" 16) # git.berlin.ccc.de + (mkEntry "baikal" 24) # dav.berlin.ccc.de ]; mkEntry = name: octet: { user = { diff --git a/secrets/postgres-baikal.age b/secrets/postgres-baikal.age new file mode 100644 index 0000000000000000000000000000000000000000..8624f2243972c29d70782aeecef3039b38b0636a GIT binary patch literal 1047 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH_0Y~UELSKD4lvB} z$tgB8^vO1NNvt$APp&dbGV{nzFG~#e2o81hDG3es3o0yfH{r@EOLEPv%q??t4G+!t z^GS0ut1Q>g3Un+f_BM(%2{tz{$n#ClEU1hOibS`~wah0eyj&qGB_ce*d;63G0`_Q z-!LyM(8SQa#KJ7y(BI7`-^9_~Bp=2 zT;mLv+={@!;L56Ue*<*evMR!TjRF<2jJ^EximUv?49cqfeGPIgDvi9;ihV(?hb&{3;E?Ej@fZa>~q- z!?d#s%`j}!uXGM|PFIN1b}>z>EDA0)D+=-qNpi_DDGhaXFHZM!HmS<1^mfwD4or56 za4!na&F6A;G_ES}GAT+nH1pQ?$jo&~F{tn;HPrVr@yn}pPBALY2~G0LG!M+s#|TL` z_X1PHbcKxYRBe-r%nXAHvn+ilmqhRE@^UwKrxYKbe53RbKQq%*1CLB&XYJxVSFS_@ z-^ffCi=1NTAY;?~YhGcbz?_6+w)N=nYk$?(fADNS`L%QG`FNi6p+_4Ra0PK&GvE#|VUFv<4LFDWR_ zF)xiU_0RD3F3vD=@ybZ{cQ;PX_co{s&Ght4@+!&pGeNh_$)mthyIi5nE2qG>G||g9 z)zBp*CA}!v+bz>1DWsqxGvClT%GW6++s`n^G&r-wFqzA=INR0O)669=!_7!PG1xaT zBcdYT*fS$6F*nF3J2WZ7$jihqD7VrrFP%$QS689H(X}ea*W05+J5xW^(kZhlFSjT+ zyecvyFW)dE-8s=K)V##SE6B4fGN0>5pwG0zGR4@f7j`$DDVsOLk#oVLxh4FP3mGew zj(J@v*y{XEtD^Vwoc74AQGPtu49SzF^zz!5UV61QH0|=1U5XAn-jy>v4RBkqKQH5H ZDdR<5$u);VkCr5d^m8rUbs}urCjh~=U19(L literal 0 HcmV?d00001 diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 655aa73..c6bac82 100644 --- a/secrets/secrets.nix +++ b/secrets/secrets.nix @@ -54,11 +54,10 @@ in _sql _monitoring ]; - "postgres-forgejo.age".publicKeys = - xengi - ++ shokinn - ++ [ _sql ]; - + "postgres-baikal.age".publicKeys = xengi ++ [ + _sql + _dav + ]; "www-staging-htpasswd.age".publicKeys = xengi ++ [ _www ]; "radicale_htpasswd.age".publicKeys = xengi ++ [ _dav ]; }