fix(deck): secret monero dedie et planning sans CalDAV

This commit is contained in:
Hermes Agent
2026-10-19 02:38:26 -04:00
parent 58ace4257f
commit 1bf853a56a
4 changed files with 105 additions and 28 deletions
+6
View File
@@ -3,8 +3,14 @@ keys:
- &tower age1usm3v0ugducrzcwud60dda7xfnj49fdhecj7kcsfm7dzwzrzeyqq2jc2hm
- &mac age1fwgg9h68zz8s6xa8qt4yn7gqcqp59s0mma8xdfq9hunksqv8fq4qd9yfaz
- &deck age154uz4pavaj5qth25ytj0q8chsya4y65pgel04zcrm02je9w6uc9sug7dp2
- &deck-host age1rhnaejc5nwcqcuq5qt45wzr2fncurlwqw0adhnavfraqsdzffatqh5yhl4
creation_rules:
- path_regex: secrets/monero-deck\.yml$
key_groups:
- age:
- *tower
- *deck-host
- path_regex: secrets/monero\.yml$
key_groups:
- age:
+2 -17
View File
@@ -73,23 +73,11 @@
sops.defaultSopsFile = ../../secrets/common.yml;
sops.secrets."monero/wallet" = {
sopsFile = ../../secrets/monero.yml;
sopsFile = ../../secrets/monero-deck.yml;
key = "wallet";
owner = "root";
mode = "0400";
};
sops.secrets."monero/nextcloud-user" = {
sopsFile = ../../secrets/common.yml;
key = "nextcloud_user";
owner = "root";
mode = "0400";
};
sops.secrets."monero/nextcloud-password" = {
sopsFile = ../../secrets/common.yml;
key = "nextcloud_password";
owner = "root";
mode = "0400";
};
programs.zsh.enable = true; # requis par users.nix
services.openssh.enable = true;
@@ -222,10 +210,7 @@
thermalGuard.maxTemperature = 88;
thermalGuard.resumeTemperature = 84;
schedule.enable = true;
calendar.enable = true;
calendar.userFile = config.sops.secrets."monero/nextcloud-user".path;
calendar.passwordFile = config.sops.secrets."monero/nextcloud-password".path;
calendar.calendars = [ "sg" ];
calendar.enable = false;
};
networking.hostName = "deck";
+25
View File
@@ -0,0 +1,25 @@
wallet: ENC[AES256_GCM,data:NucBtyOPABb4u/9jVjE4dkdcu7e8EP27r/ApgiRLnSUAOM0Ne1amnp6hj9CFg/+9bHT5uZ1yvyL88tIzwT8GnQVx7f8JxBVSjV9ADlFlyvuqYQ9DkqINCCPU5783IuY=,iv:seS+LXnXWESBRh7c1o7yRcHNMqXVecGl3jtUODiUfI4=,tag:4ZgH4A+zzbjt8tb/gq5kaA==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvejJPNXdBTFkrczNQZXA5
UFhDakw1TXNXRGRFN2wwSHdidXdjcjFEUW5rCndFelZrbzc5Uy8xdzUwVThsRkw2
S21aTG5QKytRRHlDSDJKazhpcFhjVTgKLS0tIFlWbVIvd0Ywb0puMGkzRzUrZjJ5
ckcxUWgyRlpIMVBUQm1Oa09obHpFZkUKaX1QVp+/OY/71uI9KK3m9a/6tyYadQta
zj/oOToZP0yyyY1my0JF/IgTQAQ0Z9rAvuXAuPeSa0bwzr0sOYVvpw==
-----END AGE ENCRYPTED FILE-----
recipient: age1usm3v0ugducrzcwud60dda7xfnj49fdhecj7kcsfm7dzwzrzeyqq2jc2hm
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyU0oyc2xnd3lySTlHK2pY
U051RGFYU2FTWFUyVlZxbFV6ZEl4dUd3ckI4CkVNemhSVjNRbm01T2ptZFVTL0tX
dmNOd25PdUZjeUxYYS9XOFQ1engxaVEKLS0tIGhpSVVSQk9JS1VhWHlrOWduRG9L
MitjNmxnSXU2SnJyV25VS1AzaWI1YlkKTAflKQ92gXVjM1kO/hh7sc9Xun2btGCv
sQ7ZUUx4ouvZi9ViZqbBXw6AYDTD9ZSMywuM4g7KRT9VuQIDbgHWpg==
-----END AGE ENCRYPTED FILE-----
recipient: age1rhnaejc5nwcqcuq5qt45wzr2fncurlwqw0adhnavfraqsdzffatqh5yhl4
lastmodified: "2026-10-19T06:37:51Z"
mac: ENC[AES256_GCM,data:fFGef4WNs/S608Y3FZTz0PHBEUwDUhMBNI5uyoX+fUOhh+3a3B5nMpEOYSVf/YWSoCQT4vn3HiDYvzxFb/5gc64QaysjthgVx3J/BhmaKUsx7dKxdx+aL2BnP15Ewbfjpa5b7tSoMtXlu8Zed+e/YBJEPa+3TBhogHz3Pgcb8RY=,iv:JO389NcI5kjddRLqSQqMX7IB7cK4fyTNrVetbPb4d6s=,tag:NeEXTBA3+eAMqrPIONDzXg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
+72 -11
View File
@@ -7,12 +7,11 @@
# cargo. Nix le copie dans le store, ce qui le protege du ramasse-miettes.
# 2. Le service backend est redeclare ici au lieu d'importer
# `nixosModules.default`, qui tirerait ce package cassable.
# 3. Garage/S3 est desactive : le module du projet declare un groupe `garage`
# statique que le DynamicUser du service garage de nixpkgs refuse
# ("User or group with specified name already exists"), et une fois ce point
# contourne le service echoue en 70 ms (dossiers /var/lib/garage/{meta,data}
# absents). Le backend tourne sans S3 : s3.env est optionnel, seules les
# routes media sont indisponibles.
# 3. Garage/S3 est desormais actif : le module du projet declare un groupe
# `garage` statique que le DynamicUser du service garage de nixpkgs refuse
# ("User or group with specified name already exists"). Le module du projet
# epingle maintenant le service sur un compte statique garage (DynamicUser =
# false), donc `manageGarage = true` est utilisable tel quel.
# L'infrastructure (user/groupe winnie, base PostgreSQL + schema, secret JWT)
# vient bien du projet via l'import ci-dessous.
{ config, pkgs, lib, ... }:
@@ -20,6 +19,9 @@ let
backendBin = pkgs.runCommand "winnie-backend-pinned" { } ''
install -Dm755 ${/home/marc/git/winnie/backend/target/release/winnie-backend} $out/bin/winnie-backend
'';
ingestBin = pkgs.runCommand "winnie-ingest-pinned" { } ''
install -Dm755 ${/home/marc/git/winnie/ingest/target/release/winnie-ingest} $out/bin/winnie-ingest
'';
in {
sops.secrets."oidc_env" = {
sopsFile = ../../secrets/winnie.yml;
@@ -30,11 +32,7 @@ in {
imports = [ /home/marc/git/winnie/services/winnie/default.nix ];
services.winnie.manageGarage = false;
# winnie-bucket n'est pas conditionne par manageGarage dans le module du
# projet : sans Garage il echoue, on empeche son demarrage au boot.
systemd.services.winnie-bucket.wantedBy = lib.mkForce [ ];
services.winnie.manageGarage = true;
# OIDC env file from sops (PKCE - no client_secret)
systemd.services.winnie-oidc-secret = {
@@ -83,8 +81,13 @@ in {
"winnie-schema.service"
"winnie-jwt-secret.service"
"winnie-oidc-secret.service"
"winnie-bucket.service"
"network.target"
];
# s3.env is optional at the EnvironmentFile level, but when the bucket unit
# exists the backend must read the key it writes: start after it so a fresh
# boot does not come up without S3.
wants = [ "winnie-bucket.service" ];
requires = [ "postgresql.service" "winnie-jwt-secret.service" "winnie-oidc-secret.service" ];
serviceConfig = {
User = "winnie";
@@ -105,4 +108,62 @@ in {
RestartSec = 5;
};
};
# The nightly batch reads its window, model, and zones from Postgres
# (`ingest_config`, edited in the app's Settings screen). This unit only
# makes the OpenRouter key available: the agent's .env contains a bare token
# that systemd would reject as an EnvironmentFile, so copy the one useful
# assignment into a dedicated 0640 file.
systemd.services.winnie-ingest-env = {
description = "Extract the OpenRouter key for the Winnie nightly ingest";
wantedBy = [ "multi-user.target" ];
before = [ "winnie-ingest-nightly.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -e
install -d -m 0750 -o root -g winnie /var/lib/winnie
${pkgs.gnugrep}/bin/grep '^OPENROUTER_API_KEY=' /var/lib/hermes/.hermes/.env > /var/lib/winnie/openrouter.env
chown hermes:winnie /var/lib/winnie/openrouter.env
chmod 0640 /var/lib/winnie/openrouter.env
'';
};
systemd.services.winnie-ingest-nightly = {
description = "Winnie nightly ingest (config-driven 01:00-08:00 window)";
after = [
"postgresql.service"
"winnie-schema.service"
"winnie-ingest-env.service"
"network-online.target"
];
wants = [ "network-online.target" ];
requires = [ "postgresql.service" "winnie-ingest-env.service" ];
path = [ pkgs."poppler-utils" ];
serviceConfig = {
Type = "oneshot";
User = "hermes";
WorkingDirectory = "/var/lib/winnie";
EnvironmentFile = "/var/lib/winnie/openrouter.env";
Environment = [
"DATABASE_URL=postgresql://winnie@127.0.0.1/winnie"
"RUST_LOG=winnie_ingest=info"
];
ExecStart = "${ingestBin}/bin/winnie-ingest nightly";
RuntimeMaxSec = "27000s";
TimeoutStartSec = "infinity";
};
};
systemd.timers.winnie-ingest-nightly = {
description = "Nightly Winnie ingest window";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 01:00:00";
Persistent = true;
Unit = "winnie-ingest-nightly.service";
};
};
}