Date : 15-01-2026 (MAJ 15-01-2026 02:00) Status : IMPLEMENTEE Objectif : Maximiser l'utilisation d'O2switch pour la résilience de l'infrastructure 33800
| Ressource | Limite | Usage actuel |
|---|---|---|
| Stockage NVMe | Illimité (3.4 To dispo) | ~650 Mo |
| PostgreSQL | Illimité | 2 BDD |
| MySQL | Illimité | 2 BDD |
| Redis | Instance privée | Non activé |
| Memcached | Instance privée | Non activé |
| SMTP | Illimité | 1 adresse |
| Bande passante | Illimitée | - |
| SSH | Accès complet | Actif |
| Cron | Illimité | - |
| Métrique | Valeur | Impact |
|---|---|---|
| Bande passante upload | ~7 Mbps (~850 Ko/s) | Pas de sync temps réel |
| Latence | Variable | Pas de haute dispo |
| Usage | Viable | Raison |
|---|---|---|
| Backup BDD (pg_dump) | ✅ | Async, toutes les 6h |
| Backup GitLab | ✅ | Async, toutes les 6h |
| Replica temps réel | ❌ | Bande passante insuffisante |
| CDN/Assets | ✅ | Fichiers statiques |
| Dashboard externe | ✅ | HTML généré localement, push |
| Webhooks proxy | ✅ | Masque IP locale |
| Status page | ✅ | Monitoring externe |
| SMTP fallback | ✅ | Backup email |
| Stockage buffer | ✅ | Gros fichiers temporaires |
Conclusion : O2switch = backup froid / résilience, pas haute disponibilité.
┌─────────────────────────────────────────────────────────────────────────┐
│ INFRA LOCALE (PVE) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Supabase │ │ GitLab │ │ stock_8to │ │
│ │ PostgreSQL │ │ (155) │ │ (7.2 To) │ │
│ │ :5433 │ │ :80 │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────┴───────┐ │
│ │ Dashboard │ │ Claude │ │ Monitoring │ │
│ │ collect.sh │ │ propositions │ │ generate.sh │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
└──────────┼──────────────────┼──────────────────┼─────────────────────────┘
│ │ │
│ Git push 1h │ Git push │ rsync 2h
│ (HTML) │ (on commit) │ (configs)
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ O2SWITCH (BACKUP FROID / RÉSILIENCE) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ DASHBOARD │ │ CLAUDE │ │ BACKUP │ │
│ │ externe │ │ Viewer MD │ │ configs + │ │
│ │ (HTML) │ │ (PHP) │ │ BDD + GitLab│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ .htaccess IP │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ SMTP │ │ Webhooks │ │ CDN │ │
│ │ BACKUP │ │ PROXY │ │ ASSETS │ │
│ │ @nowhere84 │ │ (masque IP) │ │ publics │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ CRON │ │ STATUS │ │
│ │ EXTERNE │ │ PAGE │ │
│ │ monitoring │ │ publique │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
# Sur PVE - Générer clé si pas existante
ssh-keygen -t ed25519 -f ~/.ssh/o2switch_key -N ""
# Copier la clé publique sur O2switch
sshpass -p 'PASSWORD' ssh-copy-id -i ~/.ssh/o2switch_key.pub deas8499@yellow.o2switch.net
# Test
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "echo OK"
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
mkdir -p ~/backup/{postgres,gitlab,configs,claude}
mkdir -p ~/public_html/dashboard
mkdir -p ~/public_html/cdn
mkdir -p ~/public_html/webhooks
mkdir -p ~/public_html/status
"
Fichier : Modifier notif-logger pour ajouter fallback SMTP
// Ajouter SMTP O2switch comme fallback
smtp: {
primary: { /* config actuelle */ },
fallback: {
host: 'yellow.o2switch.net',
port: 465,
secure: true,
auth: {
user: '2u@nowhere84.com',
pass: process.env.O2SWITCH_SMTP_PASSWORD
}
}
}
LOCAL O2SWITCH
────── ────────
collect.sh (cron 1h)
│
▼
generate.sh
│
▼
git add + commit + push ──► Git ──► post-receive hook
│
auto-deploy
│
▼
~/public_html/dashboard/
│
.htaccess (IP)
│
▼
https://dashboard.nowhere84.com
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
mkdir -p ~/repositories
cd ~/repositories
git init --bare dashboard.git
"
Fichier O2switch : ~/repositories/dashboard.git/hooks/post-receive
#!/bin/bash
# Auto-deploy dashboard vers public_html
TARGET="/home/deas8499/public_html/dashboard"
GIT_DIR="/home/deas8499/repositories/dashboard.git"
echo "Deploying dashboard..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f main
# Copier .htaccess s'il n'existe pas
if [ ! -f "$TARGET/.htaccess" ]; then
cp /home/deas8499/config/dashboard.htaccess $TARGET/.htaccess
fi
echo "Dashboard deployed: $(date)"
# Rendre exécutable et créer config
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
chmod +x ~/repositories/dashboard.git/hooks/post-receive
mkdir -p ~/config
"
# Sur PVE - Dans le dossier monitoring
cd /stock_8to/33800-stack/monitoring
# Si pas déjà un repo git
git init
echo '.htaccess' >> .gitignore # htaccess géré séparément
# Ajouter remote O2switch
git remote add o2switch deas8499@yellow.o2switch.net:~/repositories/dashboard.git
# Commit initial
git add -A
git commit -m "Initial: dashboard monitoring"
git push -u o2switch main
Ajouter à la fin de /stock_8to/33800-stack/monitoring/generate.sh :
# Auto-deploy vers O2switch via Git
cd /stock_8to/33800-stack/monitoring
git add -A
git commit -m "Update: $(date +%Y-%m-%d_%H:%M)" 2>/dev/null || true
git push o2switch main 2>/dev/null &
Fichier O2switch : ~/config/dashboard.htaccess (copié automatiquement par le hook)
# Restriction par IP
Order Deny,Allow
Deny from all
# IP Box (fixe)
Allow from 82.65.119.221
# IP Mobile/Backup (ajouter selon besoin)
# Allow from x.x.x.x
# Basic Auth en fallback (si IP change)
AuthType Basic
AuthName "Dashboard 33800"
AuthUserFile /home/deas8499/.htpasswd
Require valid-user
# Autoriser si IP OK OU si auth OK
Satisfy any
# Cache navigateur
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 5 minutes"
ExpiresByType text/css "access plus 1 hour"
ExpiresByType application/javascript "access plus 1 hour"
</IfModule>
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
htpasswd -cb ~/.htpasswd gouroubleu 'MOT_DE_PASSE_DASHBOARD'
"
Fichier : /stock_8to/33800-stack/scripts/backup/sync-configs-o2switch.sh
#!/bin/bash
# Sync configs critiques vers O2switch
O2_KEY="/root/.ssh/o2switch_key"
O2_HOST="deas8499@yellow.o2switch.net"
O2_PATH="~/backup/configs"
rsync -avz -e "ssh -i $O2_KEY" --delete \
/stock_8to/33800-stack/claude/ \
/stock_8to/33800-stack/docker/secrets/ \
/stock_8to/33800-stack/docker/stacks/*/docker-compose.yml \
/stock_8to/33800-stack/scripts/ \
/stock_8to/33800-stack/monitoring/config.json \
$O2_HOST:$O2_PATH/
echo "$(date): Configs synced to O2switch" >> /var/log/o2switch-sync.log
Cron PVE : 0 */2 * * * /stock_8to/33800-stack/scripts/backup/sync-configs-o2switch.sh
Fichier : /stock_8to/33800-stack/scripts/backup/pg-to-o2switch.sh
#!/bin/bash
# Dump PostgreSQL Supabase vers O2switch
O2_KEY="/root/.ssh/o2switch_key"
O2_HOST="deas8499@yellow.o2switch.net"
O2_PATH="~/backup/postgres"
TIMESTAMP=$(date +%Y%m%d_%H%M)
# Dump (depuis prod-portainer)
ssh gouroubleu@192.168.1.12 "
docker exec supabase-db-prod pg_dump -U postgres postgres
" | gzip > /tmp/supabase_$TIMESTAMP.sql.gz
# Upload
scp -i $O2_KEY /tmp/supabase_$TIMESTAMP.sql.gz $O2_HOST:$O2_PATH/
# Cleanup local
rm -f /tmp/supabase_$TIMESTAMP.sql.gz
# Garder 14 derniers dumps sur O2switch (3.5 jours à 6h)
ssh -i $O2_KEY $O2_HOST "cd ~/backup/postgres && ls -t *.gz 2>/dev/null | tail -n +15 | xargs -r rm"
echo "$(date): PostgreSQL backup to O2switch" >> /var/log/o2switch-sync.log
Cron PVE : 0 */6 * * * /stock_8to/33800-stack/scripts/backup/pg-to-o2switch.sh
Fichier : /stock_8to/33800-stack/scripts/backup/gitlab-to-o2switch.sh
#!/bin/bash
# Sync GitLab backups vers O2switch
# GitLab crée des backups toutes les 6h dans /var/opt/gitlab/backups/
O2_KEY="/root/.ssh/o2switch_key"
O2_HOST="deas8499@yellow.o2switch.net"
O2_PATH="~/backup/gitlab"
GITLAB_HOST="gouroubleu@192.168.1.196"
GITLAB_BACKUP_DIR="/var/opt/gitlab/backups"
# Récupérer le dernier backup GitLab
LATEST=$(ssh $GITLAB_HOST "ls -t $GITLAB_BACKUP_DIR/*.tar 2>/dev/null | head -1")
if [ -n "$LATEST" ]; then
FILENAME=$(basename "$LATEST")
# Copier vers PVE puis vers O2switch (évite accès direct GitLab→O2switch)
scp $GITLAB_HOST:$LATEST /tmp/$FILENAME
scp -i $O2_KEY /tmp/$FILENAME $O2_HOST:$O2_PATH/
rm -f /tmp/$FILENAME
echo "$(date): GitLab backup synced: $FILENAME" >> /var/log/o2switch-sync.log
fi
# Garder 7 derniers backups sur O2switch (1 semaine)
ssh -i $O2_KEY $O2_HOST "cd ~/backup/gitlab && ls -t *.tar 2>/dev/null | tail -n +8 | xargs -r rm"
Cron PVE : 30 */6 * * * /stock_8to/33800-stack/scripts/backup/gitlab-to-o2switch.sh
Note : Exécuté 30 min après le backup GitLab (0 */6) pour laisser le temps à la création.
~/public_html/cdn/
├── images/
├── fonts/
├── js/
├── css/
└── downloads/
#!/bin/bash
# Sync assets publics vers CDN O2switch
O2_KEY="/root/.ssh/o2switch_key"
O2_HOST="deas8499@yellow.o2switch.net"
rsync -avz -e "ssh -i $O2_KEY" --delete \
/stock_8to/33800-stack/public-assets/ \
$O2_HOST:~/public_html/cdn/
URL : https://cdn.nowhere84.com/
Fichier O2switch : ~/public_html/webhooks/receive.php
<?php
// Webhook proxy - forwarde vers infra locale sans exposer IP
$local_endpoint = 'http://82.65.119.221:5400/webhooks/incoming';
$payload = file_get_contents('php://input');
// Log
$log = date('Y-m-d H:i:s') . " - " . $_SERVER['REQUEST_URI'] . " - " . strlen($payload) . " bytes\n";
file_put_contents('/home/deas8499/logs/webhooks.log', $log, FILE_APPEND);
// Forward
$ch = curl_init($local_endpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Forwarded-From: o2switch',
'X-Webhook-Source: ' . ($_SERVER['HTTP_X_WEBHOOK_SOURCE'] ?? 'unknown')
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30
]);
$response = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
http_response_code($code ?: 200);
echo $response ?: '{"status":"forwarded"}';
URL : https://nowhere84.com/webhooks/receive.php
Fichier O2switch : ~/public_html/status/index.php
<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$services = [
'nginx' => ['url' => 'http://82.65.119.221:80', 'name' => 'Reverse Proxy'],
'portainer' => ['url' => 'http://82.65.119.221:9443', 'name' => 'Docker PROD'],
'supabase' => ['url' => 'http://82.65.119.221:8200', 'name' => 'Supabase API'],
'ntfy' => ['url' => 'http://82.65.119.221:8080', 'name' => 'Notifications'],
'grafana' => ['url' => 'http://82.65.119.221:3000', 'name' => 'Monitoring']
];
$results = [];
$all_up = true;
foreach ($services as $id => $svc) {
$start = microtime(true);
$ch = curl_init($svc['url']);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_NOBODY => true,
CURLOPT_SSL_VERIFYPEER => false
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$latency = round((microtime(true) - $start) * 1000);
curl_close($ch);
$up = $code >= 200 && $code < 400;
if (!$up) $all_up = false;
$results[$id] = [
'name' => $svc['name'],
'status' => $up ? 'up' : 'down',
'code' => $code,
'latency_ms' => $latency
];
}
echo json_encode([
'timestamp' => date('c'),
'overall' => $all_up ? 'operational' : 'degraded',
'services' => $results
], JSON_PRETTY_PRINT);
URL : https://status.nowhere84.com/ (créer sous-domaine)
Cron O2switch : */5 * * * *
#!/bin/bash
# /home/deas8499/scripts/healthcheck.sh
SERVICES="http://82.65.119.221:80 http://82.65.119.221:9443 http://82.65.119.221:8080"
ALERT_URL="https://ntfy.33800.nowhere84.com/infra-alerts"
LOG="/home/deas8499/logs/healthcheck.log"
for url in $SERVICES; do
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -k $url)
if [ "$status" != "200" ] && [ "$status" != "301" ] && [ "$status" != "302" ]; then
echo "$(date): DOWN $url (code: $status)" >> $LOG
curl -s -d "🔴 Service DOWN: $url (code: $status)" $ALERT_URL
fi
done
# Sur PVE - Initialiser le repo
cd /stock_8to/33800-stack/claude
git init
git add -A
git commit -m "Initial: propositions, tasks, backlog"
# Créer repo sur GitLab (via API ou UI)
# URL: https://gitlab.33800.nowhere84.com/gouroubleu/claude-docs
# Ajouter remotes
git remote add origin git@gitlab.33800.nowhere84.com:gouroubleu/claude-docs.git
git remote add o2switch deas8499@yellow.o2switch.net:~/repositories/claude-docs.git
# Push initial
git push -u origin main
git push -u o2switch main
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
mkdir -p ~/repositories
cd ~/repositories
git init --bare claude-docs.git
"
Fichier O2switch : ~/repositories/claude-docs.git/hooks/post-receive
#!/bin/bash
# Auto-deploy claude-docs vers le dossier backup
TARGET="/home/deas8499/backup/claude"
GIT_DIR="/home/deas8499/repositories/claude-docs.git"
echo "Deploying claude-docs..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f main
echo "Deploy complete: $(date)"
# Rendre exécutable
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
chmod +x ~/repositories/claude-docs.git/hooks/post-receive
mkdir -p ~/backup/claude
"
Fichier local : /stock_8to/33800-stack/claude/.git/hooks/post-commit
#!/bin/bash
# Auto-push vers GitLab et O2switch après chaque commit
echo "Auto-pushing to remotes..."
git push origin main 2>/dev/null &
git push o2switch main 2>/dev/null &
wait
echo "Push complete"
chmod +x /stock_8to/33800-stack/claude/.git/hooks/post-commit
# Après création/modification d'une proposition ou task
cd /stock_8to/33800-stack/claude
git add -A
git commit -m "Add: proposition resilience O2switch"
# → Auto-push vers GitLab + O2switch (via hook)
# → Auto-deploy sur O2switch (via post-receive)
# → Visible immédiatement dans le viewer Markdown
Fichier O2switch : ~/public_html/dashboard/claude.php
<?php
// Viewer Markdown pour propositions/tasks/backlog
require_once 'Parsedown.php'; // Lib Markdown → HTML
$baseDir = '/home/deas8499/backup/claude';
$folders = ['propositions', 'tasks', 'backlog'];
$currentFolder = $_GET['folder'] ?? 'propositions';
$currentFile = $_GET['file'] ?? '';
// Sécurité : valider le dossier
if (!in_array($currentFolder, $folders)) {
$currentFolder = 'propositions';
}
$folderPath = "$baseDir/$currentFolder";
$files = glob("$folderPath/*.md");
// Trier par date décroissante (nom de fichier)
usort($files, function($a, $b) {
return filemtime($b) - filemtime($a);
});
// Charger le contenu si fichier sélectionné
$content = '';
$contentHtml = '';
if ($currentFile && file_exists("$folderPath/$currentFile")) {
$content = file_get_contents("$folderPath/$currentFile");
$parsedown = new Parsedown();
$parsedown->setSafeMode(true);
$contentHtml = $parsedown->text($content);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Claude - <?= ucfirst($currentFolder) ?></title>
<style>
* { box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #0d1117; color: #c9d1d9; margin: 0;
display: flex; height: 100vh;
}
.sidebar {
width: 300px; background: #161b22; padding: 20px;
overflow-y: auto; border-right: 1px solid #30363d;
}
.main { flex: 1; padding: 30px; overflow-y: auto; }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; }
.tab {
padding: 8px 16px; background: #21262d; border-radius: 6px;
text-decoration: none; color: #c9d1d9;
}
.tab.active { background: #238636; color: white; }
.file-list { list-style: none; padding: 0; }
.file-list li { margin: 5px 0; }
.file-list a {
display: block; padding: 8px 12px; background: #21262d;
border-radius: 4px; text-decoration: none; color: #58a6ff;
font-size: 13px; white-space: nowrap; overflow: hidden;
text-overflow: ellipsis;
}
.file-list a:hover, .file-list a.active { background: #30363d; }
.content {
background: #161b22; padding: 30px; border-radius: 8px;
line-height: 1.6;
}
.content h1, .content h2, .content h3 { color: #58a6ff; border-bottom: 1px solid #30363d; padding-bottom: 10px; }
.content code { background: #0d1117; padding: 2px 6px; border-radius: 4px; }
.content pre { background: #0d1117; padding: 15px; border-radius: 6px; overflow-x: auto; }
.content table { border-collapse: collapse; width: 100%; }
.content th, .content td { border: 1px solid #30363d; padding: 8px 12px; text-align: left; }
.content th { background: #21262d; }
.badge {
display: inline-block; padding: 2px 8px; border-radius: 10px;
font-size: 11px; margin-left: 5px;
}
.badge-propositions { background: #1f6feb; }
.badge-tasks { background: #238636; }
.badge-backlog { background: #a371f7; }
.empty { color: #8b949e; font-style: italic; }
</style>
</head>
<body>
<div class="sidebar">
<h2>Claude</h2>
<div class="tabs">
<?php foreach ($folders as $f): ?>
<a href="?folder=<?= $f ?>" class="tab <?= $f === $currentFolder ? 'active' : '' ?>">
<?= ucfirst($f) ?>
<span class="badge badge-<?= $f ?>"><?= count(glob("$baseDir/$f/*.md")) ?></span>
</a>
<?php endforeach; ?>
</div>
<ul class="file-list">
<?php foreach ($files as $file): ?>
<?php $filename = basename($file); ?>
<li>
<a href="?folder=<?= $currentFolder ?>&file=<?= urlencode($filename) ?>"
class="<?= $filename === $currentFile ? 'active' : '' ?>"
title="<?= $filename ?>">
<?= $filename ?>
</a>
</li>
<?php endforeach; ?>
<?php if (empty($files)): ?>
<li class="empty">Aucun fichier</li>
<?php endif; ?>
</ul>
</div>
<div class="main">
<?php if ($contentHtml): ?>
<div class="content">
<?= $contentHtml ?>
</div>
<?php else: ?>
<div class="empty">
<h2>Sélectionnez un fichier</h2>
<p>Choisissez un fichier dans la liste à gauche pour voir son contenu.</p>
</div>
<?php endif; ?>
</div>
</body>
</html>
ssh -i ~/.ssh/o2switch_key deas8499@yellow.o2switch.net "
cd ~/public_html/dashboard
curl -sL https://raw.githubusercontent.com/erusev/parsedown/master/Parsedown.php -o Parsedown.php
"
Fichier O2switch : ~/public_html/dashboard/index.php
<?php
// Dashboard principal - Hub de navigation
?>
<!DOCTYPE html>
<html>
<head>
<title>Dashboard 33800</title>
<meta http-equiv="refresh" content="300"> <!-- Refresh 5min -->
<style>
body {
font-family: -apple-system, sans-serif;
background: #0d1117; color: #c9d1d9;
padding: 40px; max-width: 1200px; margin: 0 auto;
}
h1 { color: #58a6ff; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; }
.card {
background: #161b22; padding: 20px; border-radius: 8px;
border: 1px solid #30363d;
}
.card h3 { margin-top: 0; color: #58a6ff; }
.card a { color: #58a6ff; text-decoration: none; }
.card a:hover { text-decoration: underline; }
.status { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; }
.status.up { background: #238636; }
.status.down { background: #da3633; }
.timestamp { color: #8b949e; font-size: 12px; }
</style>
</head>
<body>
<h1>Dashboard 33800</h1>
<p class="timestamp">Dernière mise à jour : <?= date('d/m/Y H:i:s') ?></p>
<div class="grid">
<div class="card">
<h3>Monitoring</h3>
<ul>
<li><a href="index.html">Dashboard Services</a></li>
<li><a href="crons.html">Crons & Schedulers</a></li>
<li><a href="raidz.html">ZFS RAID Status</a></li>
</ul>
</div>
<div class="card">
<h3>Claude</h3>
<ul>
<li><a href="claude.php?folder=propositions">Propositions</a></li>
<li><a href="claude.php?folder=tasks">Tasks</a></li>
<li><a href="claude.php?folder=backlog">Backlog</a></li>
</ul>
</div>
<div class="card">
<h3>Monitoring</h3>
<ul>
<li><a href="http://grafana.33800.nowhere84.com" target="_blank">Grafana</a></li>
<li><a href="/status/">Status API (JSON)</a></li>
</ul>
</div>
<div class="card">
<h3>Status Externe</h3>
<ul>
<li><a href="/status/">API Status (JSON)</a></li>
<li><a href="https://status.nowhere84.com" target="_blank">Page Status Publique</a></li>
</ul>
</div>
<div class="card">
<h3>Outils</h3>
<ul>
<li><a href="https://portainer.33800.nowhere84.com" target="_blank">Portainer</a></li>
<li><a href="https://gitlab.33800.nowhere84.com" target="_blank">GitLab</a></li>
<li><a href="https://ntfy.33800.nowhere84.com" target="_blank">Ntfy</a></li>
</ul>
</div>
</div>
</body>
</html>
| Composant | Source | Destination | Fréquence | Taille estimée |
|---|---|---|---|---|
| Dashboard | generate.sh + Git | O2switch (post-receive) | 1h | ~500 Ko |
| Claude Docs | propositions/tasks/backlog + Git | O2switch (post-receive) | À chaque commit | ~1-5 Mo |
| Configs | scripts/secrets | O2switch rsync | 2h | ~10 Mo |
| PostgreSQL | pg_dump Supabase | O2switch | 6h | ~50-200 Mo |
| GitLab | gitlab-backup .tar | O2switch | 6h (+30min) | ~500 Mo - 2 Go |
| SMTP | fallback | O2switch | Temps réel | - |
| Webhooks | externe | Proxy O2switch | Temps réel | - |
| CDN | assets | O2switch | Manuel | Variable |
| Status | PHP check | O2switch | Temps réel | - |
| Monitoring | Cron O2switch | Alertes ntfy | 5min | - |
| Viewer MD | claude.php | O2switch | - | ~10 Ko |
| Élément | Protection |
|---|---|
| Dashboard | IP whitelist + Basic Auth fallback |
| SSH | Clé ED25519, IP whitelistée |
| Webhooks | Signature vérification optionnelle |
| Status | Public (JSON, pas de données sensibles) |
| Backups | Hors public_html, non accessible web |
| Sous-domaine | Cible | Usage |
|---|---|---|
| dashboard.nowhere84.com | ~/public_html/dashboard | Dashboard monitoring |
| cdn.nowhere84.com | ~/public_html/cdn | Assets statiques |
| status.nowhere84.com | ~/public_html/status | Status page publique |
dashboard.git, claude-docs.git| Phase | Contenu | Temps |
|---|---|---|
| Phase 1 | SSH + structure + SMTP | 1h |
| Phase 2 | Dashboard Git + .htaccess | 1h30 |
| Phase 3 | Backup configs + BDD + GitLab | 1h30 |
| Phase 4-5 | CDN + Webhooks | 1h |
| Phase 6-7 | Status + Monitoring cron | 1h |
| Phase 8 | Git claude/ + Viewer MD | 1h |
| Phase 9 | Navigation dashboard | 30min |
| Total | ~7h30 |
En attente de validation.