---
name: planification-taches
description: Programmer une action sur le VPS (cron, tâche unique, alerte ntfy ou mail) — choix de l'outil, nomenclature oneshot-*/cron-*, fuseau horaire (serveur en UTC, conversion heure de Paris), checklist de sécurité (secret jamais dans l'environnement, PATH de cron, récurrence annuelle), auto-nettoyage/auto-destruction, contrôle d'hygiène des tâches, rotation des logs, fichiers en place et vérifications.
---

# Planification de tâches sur le VPS

## Quand charger ce skill

Toute demande du type « programmer / planifier / rappeler / envoyer une alerte plus tard », ou toute modification d'un crontab, d'un timer systemd ou d'un script de supervision.

## 1. Choisir l'outil

| Besoin | Outil | Note |
|---|---|---|
| Récurrent (quotidien…) | **`crontab`** (root ou `opencode`) | environnement **minimal** : aucune variable reprise |
| **Unique** (un rappel daté) | `crontab` avec date `30 8 18 10 *` **+ auto-retrait** | la syntaxe est **annuelle** : sans retrait, elle se rejoue l'année suivante |
| ~~Unique avec `at`~~ | **INTERDIT** | `at` recopie **tout l'environnement** en clair dans sa file → fuite de secrets (constaté le 17/10) |
| Récurrent + journalisé | `systemd timer` | alternative propre si le besoin grandit |

**Nomenclature des scripts** (depuis le 17/10) :

| Type | Préfixe | Exemple | Cycle de vie |
|---|---|---|---|
| **One-shot** (rappel daté) | `oneshot-<date>-<sujet>.sh` | `oneshot-20261018-reminder.sh` | auto-nettoyage crontab → sentinel → `rm -f "$0"` |
| **Récurrent** (cron) | `cron-<période>-<sujet>.sh` | `cron-hebdo-memory-consolidation.sh` | reste en place, **jamais** de sentinel ni d'auto-destruction |

Le contrôle d'hygiène (couche C) ne surveille **que** `oneshot-*.sh` : un script `cron-*` ne doit **jamais** être signalé comme résidu.

## 2. Checklist avant de valider

0. **Fuseau horaire** : le serveur est en **UTC** — convertir l'heure cible (heure de Paris) en UTC avant d'écrire la ligne cron : **UTC+2 en été, UTC+1 en hiver** (voir §7).
1. Commandes en **chemin absolu** (`/usr/bin/…`, `/usr/local/sbin/…`).
2. `export HOME=…` + `export PATH=…` **en tête** de script : cron n'a ni `~/.local/bin` ni le PATH de login.
3. Secret lu **à l'exécution** : `$(pass show <nom>)` — jamais en dur, jamais exporté dans l'environnement.
4. **Auto-nettoyage** d'une tâche unique : `crontab -l | grep -v <marqueur> | crontab -` en fin de script, puis — **toute dernière ligne** — `rm -f "$0"` : le script se supprime lui-même (plus de résidu possible ; la trace reste dans **git**). ⚠️ Le `<marqueur>` doit être le **nom exact du script** (ou son chemin complet) — un fragment approximatif ne matche pas et laisse la ligne en place (leçon du 18/10).
5. **Sentinel** d'unicité : `$HOME/.local/state/<id>.done` (`.local/` est ignoré par git) — si déjà présent : rien envoyer, retirer la ligne résiduelle, `exit 0`.
6. Vérifications : `bash -n <script>` puis **test en conditions cron** : `env -i PATH=/usr/bin:/bin HOME=<user> <script>`.
7. Relecture finale : `crontab -l` (aucune variable reprise) et `atq` vide.
8. **Rotation** : tout fichier de log écrit régulièrement doit avoir sa règle `/etc/logrotate.d/` (vérif : `logrotate --debug /etc/logrotate.d/<nom>`).

## 3. Fichiers en place (état du 18/10/2026)

| Fichier | Propriétaire | Rôle | Cron |
|---|---|---|---|
| `/usr/local/sbin/vps-check.sh` | root | journal d'état → `/var/log/vps-check.log` | 03:05 |
| `/usr/local/sbin/ntfy-alert.sh` | opencode | alerte **uniquement** si service en échec, disque ≥ 80 % **ou dérive d'hygiène** | 03:10 |
| `~/.opencode/bin/cron-hebdo-memory-consolidation.sh` | opencode | rappel mensuel (1er vendredi, mail) — **récurrent**, jamais auto-détruit | `0 18 * * 5` |
| `/etc/logrotate.d/vps-check` | root | rotation **mensuelle**, 6 rétentions, compressée | mensuel |
| `/etc/cron.d/vps-check` | root | les 2 premières lignes | — |
| `~/.local/state/…done` | opencode | sentinel d'unicité | — |

> Horaires avancés le 18/10 (choix GRK) : vps-check **03:05**, ntfy-alert **03:10** (UTC) — l'alerte est disponible au réveil. Le one-shot `oneshot-20261018-reminder.sh` a été consommé et auto-détruit (ligne crontab retirée le 18/10).

### Hygiène des tâches one-shot — 3 couches (17/10)

| Couche | Où | Rôle | État |
|---|---|---|---|
| **A** · rotation du journal | `/etc/logrotate.d/vps-check` (root) | mensuel, `rotate 6`, `compress`, `create 0640 root adm` | ✅ validé par `logrotate --debug` |
| **B** · auto-destruction | fin du script one-shot | cleanup crontab → sentinel → `rm -f "$0"` | ✅ |
| **C** · contrôle d'hygiène | `ntfy-alert.sh` (03:10) | **détecte et alerte** — jamais de suppression | ✅ testé (notification reçue) |

**Les 3 vérifications de C** : ligne de crontab → le script `.sh` existe encore ? · script présent **et** sentinel présent → résidu → alerte · sentinel **seul** → fin de vie normale, pas d'alerte.

**Registre** = la convention de nommage `~/.opencode/bin/oneshot-*.sh` + les lignes de `crontab -l` et `/etc/cron.d/` : aucun fichier de gestionnaire à maintenir, aucun cron supplémentaire. Les scripts récurrents (`cron-*`) sont **hors périmètre** de C. Un nettoyage périodique automatique (« GC ») est volontairement écarté : un outil qui supprime sans validation contredit *analyser avant d'agir* — on dépiste, on alerte, l'humain tranche.

## 4. Envoyer une alerte

- **ntfy** : skill dédié [[80_CONFIG/IA_SKILLS/Ntfy]] — serveur local `127.0.0.1:8080`, topic `vps-admin`, token à l'exécution (`pass show opencode/ntfy-token`, root via `runuser`), options (Title/Tags/Priority) et pièges (403 sans token, PATH de cron). Binaire serveur : `/var/www/ntfy/ntfy`, absent du PATH.
- **Mail** : `echo "corps" | mail -s "objet" user@domaine` → postfix local, signé DKIM ; suivi `tail /var/log/mail.log` (`status=sent`).

## 5. Vérifier / dépanner

`crontab -l` · `atq` / `atrm <id>` · `systemctl status cron` · `tail /var/log/vps-check.log` · `tail /var/log/mail.log` · `systemctl list-units --failed`.
Avant de conclure : toujours rejouer le script en environnement minimal (`env -i …`).

## 6. Les leçons

1. **PATH absent de cron** (17/10) → `pass: command not found` → token vide → **403 → alerte perdue en silence**, alors que le script rend `rc=0`.
2. **`at` recopie l'environnement** (17/10) → `GRIST_API_KEY`, `SB_AUTH_TOKEN`, `OPENROUTER_API_KEY`, `OPENCODE_SERVER_PASSWORD` visibles via `at -c` → job supprimé, repassé en `crontab`, règle ajoutée au contexte global.
3. **Marqueur de cleanup non matchant** (18/10) : le one-shot `oneshot-20261018-reminder.sh` cherchait `reminder-20261018.sh` dans son `grep -v` (date/reminder inversés) → la ligne crontab n'a jamais été retirée, résidu détecté par la couche C (« crontab orpheline »). Le marqueur doit être le **nom exact du script** (ou son chemin complet).

## 7. Fuseau horaire — le serveur est en UTC (18/10/2026)

Le serveur est réglé sur **`Etc/UTC`** (`timedatectl`). Toutes les heures cron sont donc interprétées en **UTC**, pas en heure de Paris — d'où un décalage constaté (alerte « 8h00 » reçue à 10h00 le 18/10).

**Conversion vers l'heure de Paris** (Europe/Paris) :

| Période | Décalage | Exemple : alerte « à 8h00 » (heure de Paris) |
|---|---|---|
| Heure d'été (dernier dim. mars → dernier dim. oct.) | **UTC+2** | `0 6 * * *` |
| Heure d'hiver (dernier dim. oct. → dernier dim. mars) | **UTC+1** | `0 7 * * *` |

**Règle pratique** : exprimer l'heure cible en heure de Paris dans la demande, puis **convertir en UTC** avant d'écrire la ligne cron. Vérifier la période en cours : `TZ=Europe/Paris date` (le `date` du serveur affiche l'heure UTC).

**`CRON_TZ` n'est PAS supporté** par le vixie-cron Debian (3.0pl1-162) : testé le 18/10/2026 — la ligne est ignorée silencieusement et la commande part en UTC. Pas de solution magique : **conversion manuelle obligatoire**.

**Piège du changement d'heure** : une tâche one-shot programmée « à 8h00 » doit être recalculée si elle chevauche un changement d'heure (dernier dimanche de mars / octobre). Pour une tâche récurrente, vérifier la période au moment de la programmation.

## Voir aussi

Règles de sécurité : [[80_CONFIG/IA_CONTEXT/AGENTS.md]] · Plan d'actions : [[04_NOTES/OVH/VPS-OVH_Securisation_20261016]] · Audit : [[04_NOTES/VPS-IA_Audit-Securite-VPS-2026-10-16]]
