promo/offres.html — les écosystèmes livrables, nommés par persona (Socle, Identité, Maison OBNL, Cabinet, Atelier, Vigie, Intégral, Présence Web). Le périmètre annoncé est tiré des modèles réels : le nombre de machines est celui du plan, pas une estimation. Les états sont honnêtes — collaboration marquée « en chantier », presence-web marquée « feuille de route, non vendable ». promo/tarifs.html — grille tarifaire de Chezlepro inc., alignée sur les six offres du site chezlepro.ca (diagnostic, socle opérateur, tenant portable, audit-ready ops, exploitation gérée, observabilité) plutôt que sur les noms de modèles Set-OPS. Diagnostic dès 4 500 $ et déductible, socle dès 18 000 $, tenant dès 12 000 $ (7 500 $ les suivants), parcours combiné dès 38 000 $ au lieu de 42 000 $, exploitation à 750/1 500/2 800 $ par mois. Trois postes, aucune facturation par siège, licences à 0 $. Montants = estimations de marché (taux effectif ~1 000-1 100 $/jour, firme spécialisée québécoise), pas des prix validés par une vente réelle. Dollars canadiens avant taxes. Le README le consigne, ainsi que le fait que cette grille est publique et versionnée. Corrige au passage une contradiction introduite : offres.html affirmait « un prix affiché serait un prix inventé » ; son tableau porte désormais les prix de départ et renvoie à tarifs.html, source de vérité. Validé : node --check du JS des 4 pages, balises équilibrées, liens internes et croisés vérifiés, prouver.py --verifier → CONFORME 16/16. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
665 lines
42 KiB
HTML
665 lines
42 KiB
HTML
<!doctype html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Set-OPS — Ton écosystème numérique souverain, prouvé</title>
|
|
<meta name="description" content="Set-OPS : moteur Ansible d'écosystèmes numériques souverains — identité, PKI, DNS, données, courriel, edge TLS, observabilité, forge, sauvegardes, pare-feu. Sur ta propre grappe Proxmox, sans SaaS, prouvé.">
|
|
<meta name="color-scheme" content="dark">
|
|
</head>
|
|
<body>
|
|
<style>
|
|
:root{
|
|
--night:#05060f; --night-2:#0a0d24; --night-3:#0e1640;
|
|
--ink:#e9ecff; --dim:#99a1c9; --dim-2:#727aa6;
|
|
--line:rgba(255,255,255,.10); --line-2:rgba(255,255,255,.06);
|
|
--panel:rgba(255,255,255,.045); --panel-2:rgba(255,255,255,.03);
|
|
--teal:#4fe3c1; --cyan:#56c2ff; --violet:#a98bff;
|
|
--aurora:linear-gradient(100deg,var(--teal) 0%,var(--cyan) 46%,var(--violet) 100%);
|
|
--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Noto Sans","Helvetica Neue",Arial,sans-serif;
|
|
--mono:ui-monospace,"SF Mono","JetBrains Mono",Menlo,Consolas,"Liberation Mono",monospace;
|
|
--wrap:1140px;
|
|
}
|
|
*{box-sizing:border-box}
|
|
html{scroll-behavior:smooth}
|
|
body{
|
|
margin:0; font-family:var(--sans); color:var(--ink); line-height:1.6;
|
|
background:
|
|
radial-gradient(1200px 760px at 82% -8%,#141c4c 0%,transparent 58%),
|
|
radial-gradient(900px 620px at 8% -2%,#0e1640 0%,transparent 55%),
|
|
linear-gradient(180deg,var(--night-2) 0%,var(--night) 62%) fixed;
|
|
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
|
|
overflow-x:hidden;
|
|
}
|
|
/* ---- fond animé : ciel + aurore + constellation ---- */
|
|
#sky{position:fixed;inset:0;z-index:-2;pointer-events:none}
|
|
#sky canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
|
|
.aurora{
|
|
position:fixed;left:-10%;right:-10%;top:-22%;height:74vh;z-index:-1;pointer-events:none;
|
|
filter:blur(64px) saturate(140%);opacity:.42;
|
|
background:
|
|
radial-gradient(40% 60% at 20% 30%,var(--teal) 0%,transparent 70%),
|
|
radial-gradient(45% 70% at 55% 20%,var(--cyan) 0%,transparent 70%),
|
|
radial-gradient(40% 65% at 82% 35%,var(--violet) 0%,transparent 70%);
|
|
animation:drift 24s ease-in-out infinite alternate;
|
|
}
|
|
@keyframes drift{from{transform:translate(-3%,0) scale(1)}to{transform:translate(3%,2%) scale(1.08)}}
|
|
|
|
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:24px}
|
|
|
|
/* ---- nav ---- */
|
|
header.nav{position:sticky;top:0;z-index:20;backdrop-filter:blur(12px);
|
|
background:linear-gradient(180deg,rgba(5,6,15,.72),rgba(5,6,15,.28));
|
|
border-bottom:1px solid var(--line-2)}
|
|
.nav .wrap{display:flex;align-items:center;gap:20px;height:64px}
|
|
.brand{display:flex;align-items:center;gap:11px;font-weight:650;letter-spacing:.2px;text-decoration:none;color:var(--ink)}
|
|
.brand svg{width:30px;height:30px;flex:none}
|
|
.brand b{font-weight:750}
|
|
.nav nav{margin-left:auto;display:flex;gap:26px;align-items:center}
|
|
.nav nav a{color:var(--dim);text-decoration:none;font-size:.94rem;font-weight:500}
|
|
.nav nav a:hover{color:var(--ink)}
|
|
.nav .cta-mini{padding:8px 16px;border-radius:999px;background:var(--aurora);color:#04121a;font-weight:700;font-size:.9rem}
|
|
.nav .cta-mini:hover{color:#04121a;filter:brightness(1.08)}
|
|
@media(max-width:760px){.nav nav a:not(.cta-mini){display:none}}
|
|
|
|
/* ---- typographie ---- */
|
|
.eyebrow{font-family:var(--mono);font-size:.76rem;letter-spacing:.28em;text-transform:uppercase;color:var(--cyan);font-weight:600}
|
|
h1,h2,h3{text-wrap:balance;line-height:1.08;margin:0}
|
|
.grad{background:var(--aurora);-webkit-background-clip:text;background-clip:text;color:transparent}
|
|
p{margin:0}
|
|
.lede{color:var(--dim);font-size:1.12rem;max-width:60ch}
|
|
|
|
/* ---- hero ---- */
|
|
.hero{position:relative;padding:88px 0 76px}
|
|
.hero .eyebrow{display:block;margin-bottom:22px}
|
|
.hero h1{font-size:clamp(2.5rem,6.4vw,5rem);font-weight:800;letter-spacing:-.02em;max-width:16ch}
|
|
.hero .lede{margin-top:26px;font-size:clamp(1.05rem,1.9vw,1.28rem);max-width:64ch}
|
|
.cta-row{display:flex;flex-wrap:wrap;gap:14px;margin-top:36px}
|
|
.btn{display:inline-flex;align-items:center;gap:9px;padding:14px 24px;border-radius:999px;font-weight:700;text-decoration:none;font-size:1rem;border:1px solid transparent;transition:transform .15s ease,filter .15s ease,border-color .15s ease}
|
|
.btn-primary{background:var(--aurora);color:#04121a}
|
|
.btn-primary:hover{transform:translateY(-2px);filter:brightness(1.08)}
|
|
.btn-ghost{color:var(--ink);border-color:var(--line);background:var(--panel)}
|
|
.btn-ghost:hover{border-color:var(--cyan);transform:translateY(-2px)}
|
|
.flow{margin-top:40px;font-family:var(--mono);font-size:.92rem;color:var(--dim);display:flex;flex-wrap:wrap;gap:10px 14px;align-items:center}
|
|
.flow code{color:var(--teal);background:var(--panel);border:1px solid var(--line-2);padding:4px 10px;border-radius:7px;white-space:nowrap}
|
|
.flow .arw{color:var(--dim-2)}
|
|
|
|
/* ---- bandeaux de section ---- */
|
|
section{padding:76px 0;position:relative}
|
|
.sec-head{max-width:66ch;margin-bottom:44px}
|
|
.sec-head h2{font-size:clamp(1.75rem,3.4vw,2.7rem);font-weight:750;letter-spacing:-.015em;margin-top:14px}
|
|
.sec-head p{margin-top:16px;color:var(--dim);font-size:1.08rem}
|
|
.rule{height:1px;background:linear-gradient(90deg,var(--line),transparent);border:0;margin:0}
|
|
|
|
/* ---- méthode : 4 temps ---- */
|
|
.beats{display:grid;grid-template-columns:repeat(4,1fr);gap:18px}
|
|
.beat{background:var(--panel-2);border:1px solid var(--line-2);border-radius:15px;padding:22px}
|
|
.beat .n{font-family:var(--mono);font-size:.8rem;color:var(--cyan);letter-spacing:.1em}
|
|
.beat h3{font-size:1.08rem;font-weight:680;margin:12px 0 8px}
|
|
.beat p{color:var(--dim);font-size:.95rem}
|
|
@media(max-width:880px){.beats{grid-template-columns:repeat(2,1fr)}}
|
|
@media(max-width:520px){.beats{grid-template-columns:1fr}}
|
|
|
|
/* ---- piliers (capacités) ---- */
|
|
.pillars{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
|
|
@media(max-width:820px){.pillars{grid-template-columns:1fr}}
|
|
.card{position:relative;background:var(--panel);border:1px solid var(--line);border-radius:18px;padding:26px 26px 24px;overflow:hidden;transition:transform .2s ease,border-color .2s ease,box-shadow .2s ease}
|
|
.card:hover{transform:translateY(-3px);border-color:rgba(86,194,255,.42);box-shadow:0 18px 50px -24px rgba(86,194,255,.5)}
|
|
.card::before{content:"";position:absolute;inset:0 0 auto 0;height:2px;background:var(--aurora);opacity:.75}
|
|
.card .kick{display:flex;align-items:center;gap:12px;margin-bottom:14px}
|
|
.card .dot{width:34px;height:34px;border-radius:10px;flex:none;display:grid;place-items:center;background:rgba(86,194,255,.12);border:1px solid rgba(86,194,255,.28)}
|
|
.card .dot svg{width:19px;height:19px}
|
|
.card h3{font-size:1.24rem;font-weight:720}
|
|
.card .sub{color:var(--dim);font-size:.96rem;margin-top:8px}
|
|
.chips{display:flex;flex-wrap:wrap;gap:7px;margin:16px 0 4px}
|
|
.chip{font-family:var(--mono);font-size:.76rem;color:var(--ink);background:rgba(255,255,255,.05);border:1px solid var(--line);padding:4px 9px;border-radius:7px}
|
|
ul.does{list-style:none;padding:0;margin:14px 0 0;display:grid;gap:9px}
|
|
ul.does li{position:relative;padding-left:22px;color:var(--dim);font-size:.95rem}
|
|
ul.does li::before{content:"";position:absolute;left:2px;top:.62em;width:7px;height:7px;border-radius:2px;background:var(--aurora);transform:rotate(45deg)}
|
|
ul.does b{color:var(--ink);font-weight:640}
|
|
.tag{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;padding:3px 9px;border-radius:999px;margin-left:auto}
|
|
.tag.proven{color:#062b22;background:linear-gradient(90deg,var(--teal),#7ff0d6)}
|
|
.tag.soon{color:var(--violet);background:transparent;border:1px solid rgba(169,139,255,.5)}
|
|
.card .kick .tag{margin-left:auto}
|
|
|
|
/* ---- console opérateur ---- */
|
|
.console{background:linear-gradient(180deg,rgba(10,13,36,.9),rgba(5,6,15,.9));border:1px solid var(--line);border-radius:16px;overflow:hidden;box-shadow:0 24px 70px -30px rgba(0,0,0,.8)}
|
|
.console .bar{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line-2);background:rgba(255,255,255,.02)}
|
|
.console .bar i{width:11px;height:11px;border-radius:50%;display:block}
|
|
.console .bar i:nth-child(1){background:#ff5f57}.console .bar i:nth-child(2){background:#febc2e}.console .bar i:nth-child(3){background:#28c840}
|
|
.console .bar span{margin-left:8px;font-family:var(--mono);font-size:.82rem;color:var(--dim)}
|
|
.console pre{margin:0;padding:22px 20px;font-family:var(--mono);font-size:.86rem;line-height:1.85;overflow-x:auto}
|
|
.console .c{color:var(--dim-2)} /* commentaire */
|
|
.console .p{color:var(--teal)} /* prompt/cmd */
|
|
.console .o{color:var(--ink)} /* sortie */
|
|
.console .g{color:var(--cyan)} /* mise en avant */
|
|
|
|
.split{display:grid;grid-template-columns:1.05fr .95fr;gap:40px;align-items:center}
|
|
@media(max-width:860px){.split{grid-template-columns:1fr;gap:28px}}
|
|
|
|
/* ---- cycle de vie ---- */
|
|
.life{display:grid;grid-template-columns:repeat(4,1fr);gap:0}
|
|
.life .step{padding:24px 22px;border:1px solid var(--line-2);border-right:0;background:var(--panel-2);position:relative}
|
|
.life .step:first-child{border-radius:15px 0 0 15px}
|
|
.life .step:last-child{border-right:1px solid var(--line-2);border-radius:0 15px 15px 0}
|
|
.life .step .k{font-family:var(--mono);font-size:.78rem;color:var(--cyan)}
|
|
.life .step h3{font-size:1.02rem;margin:10px 0 7px;font-weight:680}
|
|
.life .step p{color:var(--dim);font-size:.9rem}
|
|
@media(max-width:820px){.life{grid-template-columns:1fr}.life .step{border-right:1px solid var(--line-2);border-bottom:0}.life .step:first-child{border-radius:15px 15px 0 0}.life .step:last-child{border-radius:0 0 15px 15px}}
|
|
|
|
/* ---- preuve : traits ---- */
|
|
.proof{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
|
|
@media(max-width:820px){.proof{grid-template-columns:1fr}}
|
|
.pf{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:24px}
|
|
.pf h3{font-size:1.1rem;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:10px}
|
|
.pf h3 svg{width:20px;height:20px}
|
|
.pf p{color:var(--dim);font-size:.95rem}
|
|
.pf code{font-family:var(--mono);color:var(--teal);font-size:.85em}
|
|
|
|
.stats{display:flex;flex-wrap:wrap;gap:14px;margin-top:34px}
|
|
.stat{flex:1 1 150px;background:var(--panel-2);border:1px solid var(--line-2);border-radius:14px;padding:18px 20px}
|
|
.stat b{display:block;font-size:1.9rem;font-weight:800;font-variant-numeric:tabular-nums}
|
|
.stat b .grad{font-weight:800}
|
|
.stat span{color:var(--dim);font-size:.86rem}
|
|
|
|
/* ---- feuille de route ---- */
|
|
.roadmap{display:flex;flex-wrap:wrap;gap:14px}
|
|
.rm{flex:1 1 260px;border:1px dashed rgba(169,139,255,.4);border-radius:14px;padding:20px 22px;background:rgba(169,139,255,.05)}
|
|
.rm .k{font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--violet)}
|
|
.rm h3{font-size:1.05rem;margin:9px 0 6px;font-weight:680}
|
|
.rm p{color:var(--dim);font-size:.9rem}
|
|
|
|
/* ---- clôture ---- */
|
|
.closing{text-align:center;padding:96px 0 40px}
|
|
.closing h2{font-size:clamp(2rem,4.4vw,3.2rem);font-weight:800;letter-spacing:-.02em;max-width:18ch;margin-inline:auto}
|
|
.closing p{margin:20px auto 0;color:var(--dim);max-width:52ch;font-size:1.08rem}
|
|
|
|
footer{border-top:1px solid var(--line-2);padding:38px 0;color:var(--dim)}
|
|
footer .wrap{display:flex;flex-wrap:wrap;gap:16px 40px;align-items:center}
|
|
footer .brand{opacity:.95}
|
|
footer .meta{margin-left:auto;display:flex;flex-wrap:wrap;gap:6px 22px;font-size:.86rem}
|
|
footer .meta span b{color:var(--ink);font-weight:600}
|
|
footer a{color:var(--cyan);text-decoration:none}
|
|
|
|
/* ---- révélations au scroll ---- */
|
|
.reveal{opacity:0;transform:translateY(18px);transition:opacity .7s ease,transform .7s ease}
|
|
.reveal.in{opacity:1;transform:none}
|
|
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:6px}
|
|
@media(prefers-reduced-motion:reduce){
|
|
.aurora{animation:none}
|
|
.reveal{opacity:1;transform:none;transition:none}
|
|
html{scroll-behavior:auto}
|
|
}
|
|
</style>
|
|
|
|
<div id="sky" aria-hidden="true"><canvas></canvas></div>
|
|
<div class="aurora" aria-hidden="true"></div>
|
|
|
|
<!-- =================== NAV =================== -->
|
|
<header class="nav">
|
|
<div class="wrap">
|
|
<a class="brand" href="#top" aria-label="Set-OPS — Alliance Boréale">
|
|
<svg viewBox="0 0 32 32" role="img" aria-hidden="true"><defs>
|
|
<radialGradient id="sky1" cx="50%" cy="36%" r="75%"><stop offset="0%" stop-color="#10184a"/><stop offset="100%" stop-color="#05060f"/></radialGradient>
|
|
<linearGradient id="au1" x1="0" y1="1" x2="1" y2="0"><stop offset="0%" stop-color="#2ec18f"/><stop offset="45%" stop-color="#56c2ff"/><stop offset="100%" stop-color="#a98bff"/></linearGradient>
|
|
<radialGradient id="gl1" cx="50%" cy="50%" r="50%"><stop offset="0%" stop-color="#bfe9ff"/><stop offset="100%" stop-color="#bfe9ff" stop-opacity="0"/></radialGradient>
|
|
<radialGradient id="st1" cx="50%" cy="45%" r="60%"><stop offset="0%" stop-color="#fff"/><stop offset="100%" stop-color="#d6ecff"/></radialGradient>
|
|
</defs>
|
|
<circle cx="16" cy="16" r="16" fill="url(#sky1)"/>
|
|
<path d="M3 21 C 9 16, 13 23, 18 18 C 22 14, 26 17, 30 13" fill="none" stroke="url(#au1)" stroke-width="2.6" stroke-linecap="round" opacity=".85"/>
|
|
<circle cx="7" cy="9" r=".9" fill="#cfe6ff"/><circle cx="24" cy="8" r="1.1" fill="#fff"/><circle cx="26" cy="22" r=".8" fill="#bfe9ff"/><circle cx="9" cy="25" r=".7" fill="#cfe6ff"/>
|
|
<circle cx="16" cy="15" r="9" fill="url(#gl1)" opacity=".55"/>
|
|
<path d="M16 5 C 16.9 12.4, 17.6 13.1, 25 14 C 17.6 14.9, 16.9 15.6, 16 23 C 15.1 15.6, 14.4 14.9, 7 14 C 14.4 13.1, 15.1 12.4, 16 5 Z" fill="url(#st1)"/>
|
|
</svg>
|
|
<span>Set‑OPS <b>·</b> <span style="color:var(--dim);font-weight:500">Alliance Boréale</span></span>
|
|
</a>
|
|
<nav>
|
|
<a href="#capacites">Capacités</a>
|
|
<a href="#methode">Méthode</a>
|
|
<a href="#preuve">Preuve</a>
|
|
<a href="offres.html">Offres</a>
|
|
<a href="tarifs.html">Tarifs</a>
|
|
<a href="valeur.html">Valeur</a>
|
|
<a class="cta-mini" href="#demarrer">Démarrer</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<a id="top"></a>
|
|
<!-- =================== HÉROS =================== -->
|
|
<div class="hero">
|
|
<div class="wrap">
|
|
<span class="eyebrow">Alliance Boréale · moteur d'écosystèmes souverains</span>
|
|
<h1>Ton écosystème numérique, <span class="grad">souverain</span> — et prouvé.</h1>
|
|
<p class="lede">Set‑OPS est un moteur Ansible qui <b style="color:var(--ink)">construit et exploite tout un écosystème interne</b> — identité, confiance, DNS, données, courriel, observabilité, forge — sur <b style="color:var(--ink)">ta propre grappe Proxmox</b>. À partir d'un plan déclaratif. Sans SaaS. Sans conteneur propriétaire. Et exploitable <b style="color:var(--ink)">sans aucune IA</b>.</p>
|
|
<div class="cta-row">
|
|
<a class="btn btn-primary" href="#capacites">Voir tout ce que tu peux faire →</a>
|
|
<a class="btn btn-ghost" href="#demarrer">De zéro à ton écosystème</a>
|
|
</div>
|
|
<div class="flow" aria-label="flux de travail">
|
|
<code>édite le plan</code><span class="arw">→</span>
|
|
<code>make instancier</code><span class="arw">→</span>
|
|
<code>make deployer</code><span class="arw">→</span>
|
|
<code>make prouver</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- =================== MÉTHODE =================== -->
|
|
<section id="methode">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">La méthode</span>
|
|
<h2>Tu décris. Le moteur génère. Ansible déploie. <span class="grad">La preuve suit.</span></h2>
|
|
<p>Set‑OPS se pilote par un <b style="color:var(--ink)">plan</b>, pas par l'édition de l'inventaire. Les registres sont la source unique de vérité ; l'inventaire Ansible en est dérivé. Tu ne touches jamais <code style="font-family:var(--mono);color:var(--teal)">hosts.yml</code> à la main.</p>
|
|
</div>
|
|
<div class="beats">
|
|
<div class="beat reveal"><div class="n">01 · décrire</div><h3>Un plan déclaratif</h3><p>Serveurs, applications, bases, domaines, nomenclature — des registres lisibles par la machine, ta seule source de vérité.</p></div>
|
|
<div class="beat reveal"><div class="n">02 · générer</div><h3>Un inventaire dérivé</h3><p>VMID, IP, VLAN et passerelle sont <b style="color:var(--ink)">dérivés</b> de la fonction. <code style="font-family:var(--mono);color:var(--teal)">make instancier</code> montre le diff avant d'appliquer.</p></div>
|
|
<div class="beat reveal"><div class="n">03 · déployer</div><h3>Par groupes, en couches</h3><p>Chaque groupe a son playbook homonyme. L'orchestrateur déploie socle → PKI → services → apps → agents, dans l'ordre.</p></div>
|
|
<div class="beat reveal"><div class="n">04 · prouver</div><h3>Une preuve rejouable</h3><p><code style="font-family:var(--mono);color:var(--teal)">make prouver</code> rejoue les vérifications et produit un rapport horodaté. Le dépôt n'affirme jamais plus qu'il ne prouve.</p></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== CAPACITÉS =================== -->
|
|
<section id="capacites">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">Tout ce que tu peux faire</span>
|
|
<h2>Sept piliers. Un écosystème complet, <span class="grad">natif, chiffré, relié.</span></h2>
|
|
<p>Tout ce qu'un hébergeur met en ligne, Set‑OPS le tient en interne — en logiciel libre, sur systemd et paquets, sans dépendance à un géant. Voici, pilier par pilier, ce que tu déploies et exploites. <b style="color:var(--ink)">Le badge <span class="tag proven" style="margin:0">Éprouvé</span> marque ce qui tourne sur des VM réelles</b> ; <span class="tag soon" style="margin:0">Feuille de route</span> ce qui est planifié.</p>
|
|
</div>
|
|
|
|
<div class="pillars">
|
|
|
|
<!-- Identité -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 4-6 8-6s8 2 8 6"/></svg></span>
|
|
<h3>Identité & accès</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Une identité, un mot de passe, pour tout le parc.</p>
|
|
<div class="chips"><span class="chip">OpenLDAP</span><span class="chip">Keycloak</span><span class="chip">oauth2-proxy</span><span class="chip">OIDC · RBAC</span></div>
|
|
<ul class="does">
|
|
<li><b>Un annuaire central</b> (OpenLDAP) source unique des identités.</li>
|
|
<li><b>SSO web</b> via Keycloak, qui fédère l'annuaire — « se connecter avec ton organisation ».</li>
|
|
<li><b>Mets n'importe quelle app derrière le SSO</b> avec oauth2-proxy, même sans OIDC natif.</li>
|
|
<li><b>RBAC</b> : des rôles de realm pilotent les niveaux d'accès (ex. Grafana Admin/Editor/Viewer).</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- PKI -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M12 2l7 3v6c0 5-3 8-7 9-4-1-7-4-7-9V5z"/><path d="M9 12l2 2 4-4"/></svg></span>
|
|
<h3>Confiance & PKI</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Ta propre autorité de certification. Tout le trafic interne, chiffré et vérifié.</p>
|
|
<div class="chips"><span class="chip">step-ca</span><span class="chip">ACME</span><span class="chip">client_pki</span><span class="chip">mTLS</span></div>
|
|
<ul class="does">
|
|
<li><b>Une AC interne</b> (step-ca) qui émet les certificats par ACME.</li>
|
|
<li><b>Certificats d'hôte auto-renouvelés</b>, avec rechargement des vrais consommateurs.</li>
|
|
<li><b>Zéro-confiance est-ouest</b> : PostgreSQL en <code>verify-full</code>, LDAPS, métriques & logs en HTTPS, LMTP vérifié.</li>
|
|
<li><b>Aucune dépendance</b> à une AC publique pour la confiance interne.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- DNS & réseau -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18"/></svg></span>
|
|
<h3>Nommage & réseau</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Un DNS interne cohérent, et un réseau convergé multi-tenant.</p>
|
|
<div class="chips"><span class="chip">PowerDNS</span><span class="chip">Unbound</span><span class="chip">VLAN dérivé</span><span class="chip">make devis-reseau</span></div>
|
|
<ul class="does">
|
|
<li><b>DNS autoritatif interne</b> (PowerDNS) : les enregistrements d'exposition se dérivent du plan.</li>
|
|
<li><b>Résolveur local</b> (Unbound) + plancher <code>/etc/hosts</code>, sans casser Internet.</li>
|
|
<li><b>Réseau convergé 10/8</b>, VLAN dérivé par tenant, isolation inter-tenant.</li>
|
|
<li><b>Config du switch générée</b> (VLANs, passerelles, ACLs) depuis les nomenclatures.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- Données -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14c0 1.7 3.6 3 8 3s8-1.3 8-3V5"/><path d="M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3"/></svg></span>
|
|
<h3>Données</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Bases relationnelles et cache, provisionnés depuis le registre.</p>
|
|
<div class="chips"><span class="chip">PostgreSQL</span><span class="chip">Redis</span><span class="chip">TLS verify-full</span><span class="chip">binding app→base</span></div>
|
|
<ul class="does">
|
|
<li><b>PostgreSQL</b> provisionne ses bases & rôles depuis le registre — le secret ne quitte jamais le rôle.</li>
|
|
<li><b>Chiffrement obligatoire</b> : la base refuse toute connexion non-TLS du réseau.</li>
|
|
<li><b>Cache réseau Redis</b> authentifié (accès sans mot de passe refusé).</li>
|
|
<li><b>Liens déclaratifs</b> : une app se relie à sa base sans group_vars codés en dur.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- Courriel -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></svg></span>
|
|
<h3>Courriel souverain</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Recevoir <i>et</i> envoyer, de bout en bout, sur ton infra.</p>
|
|
<div class="chips"><span class="chip">Postfix</span><span class="chip">Dovecot</span><span class="chip">rspamd</span><span class="chip">DKIM · SASL</span></div>
|
|
<ul class="does">
|
|
<li><b>MTA Postfix</b> + soumission <code>:587</code> authentifiée (SASL déléguée à Dovecot).</li>
|
|
<li><b>Boîtes Dovecot</b> (IMAP, Maildir), utilisateurs résolus sur l'annuaire LDAP.</li>
|
|
<li><b>Antispam & signature</b> : rspamd + DKIM. Flux prouvé SMTP→LDAP→LMTP→IMAP.</li>
|
|
<li><b>Interne de bout en bout</b> — aucune fuite vers un tiers.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- Edge / TLS -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M4 12h16M4 12a8 8 0 0116 0M4 12a8 8 0 0016 0"/><rect x="9" y="9" width="6" height="6" rx="1" fill="#56c2ff" stroke="none" opacity=".5"/></svg></span>
|
|
<h3>Reverse-proxy & TLS</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Déclare une exposition — tout le reste se dérive.</p>
|
|
<div class="chips"><span class="chip">nginx edge</span><span class="chip">TLS step-ca</span><span class="chip">X-Forwarded</span><span class="chip">WAF</span></div>
|
|
<ul class="does">
|
|
<li>Un champ <code>expose</code> produit <b>vhost + SAN de cert + enregistrement DNS + alias plancher</b>, auto-dérivés.</li>
|
|
<li><b>Terminaison TLS</b> avec le cert step-ca (chaîne validée, plus de snakeoil).</li>
|
|
<li><b>Un seul edge</b> route tout le trafic HTTP(S) externe vers les services internes.</li>
|
|
<li>Prêt pour une <b>frontière publique OPNsense</b> en bordure.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- Observabilité -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M3 17l5-6 4 4 5-8 4 6"/><path d="M3 21h18"/></svg></span>
|
|
<h3>Observabilité & supervision</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Voir toute la flotte : métriques, journaux, tableaux de bord, impact.</p>
|
|
<div class="chips"><span class="chip">Prometheus</span><span class="chip">Loki</span><span class="chip">Grafana</span><span class="chip">Icinga2 · BPM</span></div>
|
|
<ul class="does">
|
|
<li><b>Métriques</b> (Prometheus scrape la flotte en HTTPS) & <b>journaux</b> centralisés (Loki).</li>
|
|
<li><b>Tableaux de bord Grafana</b>, accès par le SSO, dashboards provisionnés.</li>
|
|
<li><b>Supervision active</b> Icinga2 + IcingaDB + Icinga Web 2, sous SSO.</li>
|
|
<li><b>Impact métier (BPM)</b> : agrège des services en processus métier — irremplaçable par un graphe.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
<!-- Applicatif -->
|
|
<article class="card reveal">
|
|
<div class="kick">
|
|
<span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M8 18l-5-6 5-6M16 6l5 6-5 6M13 4l-2 16"/></svg></span>
|
|
<h3>Applicatif & forge</h3>
|
|
<span class="tag proven">Éprouvé</span>
|
|
</div>
|
|
<p class="sub">Tes services internes, adossés à l'identité commune.</p>
|
|
<div class="chips"><span class="chip">Forgejo</span><span class="chip">SSO OIDC</span><span class="chip">branding</span></div>
|
|
<ul class="does">
|
|
<li><b>Forge Git Forgejo</b> : dépôts, CI, wiki — <b>branchée au SSO</b>, comptes auto-créés depuis l'annuaire.</li>
|
|
<li><b>Adossée à PostgreSQL</b> (base provisionnée depuis le registre) et exposée par l'edge.</li>
|
|
<li><b>Identité visuelle</b> par le mécanisme officiel (pas de fork), résistante aux mises à jour.</li>
|
|
<li>La couche <b>collaboration & web applicative</b> arrive — voir la feuille de route.</li>
|
|
</ul>
|
|
</article>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== SOCLE & MÉTHODE TRANSVERSE =================== -->
|
|
<section>
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">Le socle & la méthode</span>
|
|
<h2>Ce qui traverse <span class="grad">toutes les VM.</span></h2>
|
|
<p>Sous les piliers, un socle commun tient l'ensemble : durcissement, pare-feu, sauvegardes, et l'outillage qui rend le tout reproductible.</p>
|
|
</div>
|
|
<div class="pillars">
|
|
<article class="card reveal">
|
|
<div class="kick"><span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"/></svg></span><h3>Durcissement par défaut</h3></div>
|
|
<p class="sub">Chaque VM naît sécurisée.</p>
|
|
<div class="chips"><span class="chip">SSH clé-only</span><span class="chip">AppArmor</span><span class="chip">auditd</span><span class="chip">fail2ban</span><span class="chip">unattended-upgrades</span></div>
|
|
<ul class="does">
|
|
<li><b>Accès SSH par clé dès le départ</b> (mot de passe désactivé, <code>AuthenticationMethods publickey</code>).</li>
|
|
<li><b>AppArmor, auditd, fail2ban, sysctl, journald</b> — appliqués par le groupe socle.</li>
|
|
<li><b>nftables préparé</b> dans le template, activé au bon moment sur les clones.</li>
|
|
</ul>
|
|
</article>
|
|
<article class="card reveal">
|
|
<div class="kick"><span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M4 6h16M4 12h16M4 18h16"/><circle cx="8" cy="6" r="2" fill="#05060f"/><circle cx="16" cy="12" r="2" fill="#05060f"/><circle cx="9" cy="18" r="2" fill="#05060f"/></svg></span><h3>Pare-feu est-ouest</h3></div>
|
|
<p class="sub">Moindre privilège, dérivé du registre des flux.</p>
|
|
<div class="chips"><span class="chip">nftables</span><span class="chip">registre des flux</span><span class="chip">policy drop</span></div>
|
|
<ul class="does">
|
|
<li><b>Qui-parle-à-qui</b> déclaré dans un registre de flux (port, sens, pair, chiffrement, raison).</li>
|
|
<li><b>Règles par hôte générées</b> : <code>ip saddr</code> = moindre privilège, reste refusé.</li>
|
|
<li>Le même registre alimente <b>nftables interne et OPNsense</b> en bordure.</li>
|
|
</ul>
|
|
</article>
|
|
<article class="card reveal">
|
|
<div class="kick"><span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><path d="M21 12a9 9 0 11-3-6.7M21 4v5h-5"/></svg></span><h3>Sauvegardes 3-2-1</h3></div>
|
|
<p class="sub">L'état, pas les VM — car l'infra se reconstruit par le code.</p>
|
|
<div class="chips"><span class="chip">restic</span><span class="chip">chiffré</span><span class="chip">jobs déclaratifs</span><span class="chip">Tier 0</span></div>
|
|
<ul class="does">
|
|
<li><b>Sauvegardes restic hors-nœud, chiffrées</b>, avec rétention automatique.</li>
|
|
<li><b>Jobs déclaratifs</b> par nœud (dumps PG, LDIF, Maildir, dépôts Git, clés d'AC).</li>
|
|
<li><b>Restauration prouvée</b>, jusqu'au Tier 0 (les clés de l'autorité de certification).</li>
|
|
</ul>
|
|
</article>
|
|
<article class="card reveal">
|
|
<div class="kick"><span class="dot"><svg viewBox="0 0 24 24" fill="none" stroke="#56c2ff" stroke-width="1.8"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M8 21h8M12 18v3M7 9l2 2-2 2M12 13h4"/></svg></span><h3>Le plan & le GUI</h3></div>
|
|
<p class="sub">Une source unique, éditable en ligne de commande ou à l'écran.</p>
|
|
<div class="chips"><span class="chip">registres</span><span class="chip">GUI local</span><span class="chip">bindings</span><span class="chip">orchestrateur</span></div>
|
|
<ul class="does">
|
|
<li><b>Interface locale</b> (<code>make inventaire-ui</code>) : vues Serveurs, Applications, Domaines, Réseau, Flux, Couches.</li>
|
|
<li><b>Bindings déclaratifs</b> : les relations service→service vivent dans le plan.</li>
|
|
<li><b>Orchestrateur en couches</b> : un point d'entrée ordonné, déterministe, généré.</li>
|
|
</ul>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== CONSOLE OPÉRATEUR =================== -->
|
|
<section id="demarrer">
|
|
<div class="wrap split">
|
|
<div class="reveal">
|
|
<span class="eyebrow">La console de l'opérateur</span>
|
|
<h2 style="font-size:clamp(1.75rem,3.2vw,2.5rem);font-weight:750;letter-spacing:-.015em;margin-top:14px">De la grappe nue à <span class="grad">l'écosystème vivant.</span></h2>
|
|
<p class="lede" style="margin-top:16px">Tout passe par <code style="font-family:var(--mono);color:var(--teal)">make</code> et un plan. Un modèle générique <code style="font-family:var(--mono);color:var(--teal)">socle</code> — DNS, AC/PKI, edge TLS, courriel — est prêt à copier, renseigner, déployer. Puis tu vis dans le plan.</p>
|
|
<ul class="does" style="margin-top:20px">
|
|
<li><b>Golden template</b> Debian 13 construit une fois, cloné pour chaque VM.</li>
|
|
<li><b>Clone Proxmox + cloud-init</b> pour l'identité initiale, puis conformité par groupes.</li>
|
|
<li><b>Reconstruction from-zero en une commande</b> — crée les VM manquantes puis déploie tout.</li>
|
|
<li><b>Multi-tenant portable</b> : une instance par écosystème, prouvée par reconstruction.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="console reveal" role="img" aria-label="Exemple de session make de Set-OPS">
|
|
<div class="bar"><i></i><i></i><i></i><span>opérateur@pilote — set-ops</span></div>
|
|
<pre><span class="c"># 1 · copier un modèle, le renseigner, générer l'inventaire</span>
|
|
<span class="p">$ cp -r exemples/modeles/socle ../mon-instance && ln -s ../mon-instance instance</span>
|
|
<span class="p">$ make instancier-appliquer</span> <span class="c">FORCE=1</span>
|
|
<span class="o">production/hosts.yml généré depuis le plan.</span>
|
|
|
|
<span class="c"># 2 · construire le template, créer les VM, déployer la flotte</span>
|
|
<span class="p">$ make preparer-modele</span> <span class="c"># socle + durcissement + cloud-init</span>
|
|
<span class="p">$ make reconstruire</span> <span class="c">CONFIRMER=true</span> <span class="c"># crée les VM PUIS déploie tout</span>
|
|
<span class="o">13 hôtes déployés · 0 échec</span>
|
|
|
|
<span class="c"># 3 · valider et PROUVER</span>
|
|
<span class="p">$ make valider</span> <span class="c"># recette fonctionnelle</span>
|
|
<span class="o">Prometheus 6/6 UP · 6 vhosts HTTPS · courriel bout-en-bout · 6 dépôts restaurés</span>
|
|
<span class="p">$ make prouver</span>
|
|
<span class="g">CONFORME : 16 OK, 0 échec, 0 sauté → docs/audit/preuve-AAAA-MM-JJ.md</span>
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== CYCLE DE VIE =================== -->
|
|
<section>
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">Cycle de vie</span>
|
|
<h2>Du template nu à la flotte, <span class="grad">et retour à zéro.</span></h2>
|
|
</div>
|
|
<div class="life reveal">
|
|
<div class="step"><div class="k">socle</div><h3>Goldeniser</h3><p>Une VM Debian 13 minimale devient le golden template : socle, durcissement, cloud-init, qemu-guest-agent.</p></div>
|
|
<div class="step"><div class="k">clone</div><h3>Cloner & identifier</h3><p>Clone via l'API Proxmox ; cloud-init donne hostname, clé, IP, DNS. VMID/IP/VLAN lus dans le plan.</p></div>
|
|
<div class="step"><div class="k">conformité</div><h3>Converger</h3><p>Les playbooks de groupes appliquent l'état voulu, relançables sans effet de bord. La flotte converge.</p></div>
|
|
<div class="step"><div class="k">from-zero</div><h3>Reconstruire</h3><p>Tout supprimer, puis <code>make myDay</code> : l'écosystème renaît du code — la preuve de portabilité sans réserve.</p></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== PREUVE & SOUVERAINETÉ =================== -->
|
|
<section id="preuve">
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">Preuve & souveraineté</span>
|
|
<h2>Il ne fait pas que le dire. <span class="grad">Il le prouve.</span></h2>
|
|
<p>Un outil d'exploitation réelle, pas une démonstration. Chaque affirmation publique est tracée vers une commande de preuve rejouable — ou corrigée.</p>
|
|
</div>
|
|
<div class="proof">
|
|
<div class="pf reveal"><h3><svg viewBox="0 0 24 24" fill="none" stroke="#4fe3c1" stroke-width="1.9"><path d="M20 6L9 17l-5-5"/></svg>Vérifier</h3><p><code>make verifier</code> enchaîne lint, tests, cohérence, syntaxe <b style="color:var(--ink)">et les preuves</b>. Un dépôt qui se tient droit.</p></div>
|
|
<div class="pf reveal"><h3><svg viewBox="0 0 24 24" fill="none" stroke="#4fe3c1" stroke-width="1.9"><path d="M9 12l2 2 4-4"/><circle cx="12" cy="12" r="9"/></svg>Prouver</h3><p><code>make prouver</code> produit un rapport <b style="color:var(--ink)">horodaté et rejouable</b> — 16 preuves reliées au registre des affirmations. Une pièce justificative.</p></div>
|
|
<div class="pf reveal"><h3><svg viewBox="0 0 24 24" fill="none" stroke="#4fe3c1" stroke-width="1.9"><path d="M3 12h4l3 8 4-16 3 8h4"/></svg>Valider</h3><p><code>make valider</code> : recette fonctionnelle sur la flotte vivante — cibles UP, vhosts HTTPS, courriel livré, sauvegardes restaurées.</p></div>
|
|
</div>
|
|
<div class="stats reveal">
|
|
<div class="stat"><b class="grad">100 %</b><span>logiciel libre (AGPLv3) — rien n'est verrouillé</span></div>
|
|
<div class="stat"><b><span class="grad">0</span></b><span>dépendance SaaS · zéro conteneur propriétaire</span></div>
|
|
<div class="stat"><b><span class="grad">0</span></b><span>IA requise — la doc, <code style="font-family:var(--mono);font-size:.7em;color:var(--teal)">make</code> et le GUI suffisent</span></div>
|
|
<div class="stat"><b class="grad">natif</b><span>systemd + paquets — pas de PaaS, pas de magie</span></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="rule">
|
|
|
|
<!-- =================== FEUILLE DE ROUTE =================== -->
|
|
<section>
|
|
<div class="wrap">
|
|
<div class="sec-head reveal">
|
|
<span class="eyebrow">Feuille de route</span>
|
|
<h2>Ce qui arrive — <span class="grad">annoncé, pas surestimé.</span></h2>
|
|
<p>Fidèle au principe : on ne présente jamais comme prêt ce qui ne l'est pas. Ces capacités sont conçues et attendues, en rôles natifs comme les autres.</p>
|
|
</div>
|
|
<div class="roadmap reveal">
|
|
<div class="rm"><div class="k">Collaboration</div><h3>Nextcloud & Collabora</h3><p>Fichiers, agenda, édition en ligne — sous la même identité SSO. Build prioritaire, offre « maison-OBNL ».</p></div>
|
|
<div class="rm"><div class="k">Couche web</div><h3>Frontal & dorsal applicatifs</h3><p>Présentation (UI, assets) et application (API), servis derrière l'edge — pour mettre tes propres webapps en ligne.</p></div>
|
|
<div class="rm"><div class="k">Certification</div><h3>Label Souverain · Résilient · Exemplaire</h3><p>Trois niveaux accumulatifs pour attester la qualité d'un écosystème — garde-fou social inclus.</p></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- =================== CLÔTURE =================== -->
|
|
<div class="closing wrap">
|
|
<div class="reveal">
|
|
<h2>Reprends la <span class="grad">souveraineté</span> de ton infrastructure.</h2>
|
|
<p>Un plan, ta grappe Proxmox, et tout un écosystème numérique — libre, chiffré, prouvé. Sous le ciel de l'Alliance Boréale.</p>
|
|
<div class="cta-row" style="justify-content:center">
|
|
<a class="btn btn-primary" href="#capacites">Explorer les capacités</a>
|
|
<a class="btn btn-ghost" href="#top">Revenir en haut ↑</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<div class="wrap">
|
|
<span class="brand">
|
|
<svg viewBox="0 0 32 32" width="26" height="26" aria-hidden="true">
|
|
<circle cx="16" cy="16" r="16" fill="url(#sky1)"/>
|
|
<path d="M3 21 C 9 16, 13 23, 18 18 C 22 14, 26 17, 30 13" fill="none" stroke="url(#au1)" stroke-width="2.6" stroke-linecap="round" opacity=".85"/>
|
|
<path d="M16 5 C 16.9 12.4, 17.6 13.1, 25 14 C 17.6 14.9, 16.9 15.6, 16 23 C 15.1 15.6, 14.4 14.9, 7 14 C 14.4 13.1, 15.1 12.4, 16 5 Z" fill="url(#st1)"/>
|
|
</svg>
|
|
Set‑OPS
|
|
</span>
|
|
<div class="meta">
|
|
<span><b>Alliance Boréale</b> · Chezlepro — Daniel Allaire</span>
|
|
<span>Licence <b>AGPLv3</b></span>
|
|
<span><i>Tout est libre.</i></span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
(function(){
|
|
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
/* ---------- constellation ---------- */
|
|
var sky = document.querySelector('#sky canvas');
|
|
if(sky){
|
|
var ctx = sky.getContext('2d'), W, H, DPR, stars=[], raf;
|
|
function size(){
|
|
DPR = Math.min(window.devicePixelRatio||1, 2);
|
|
W = sky.clientWidth; H = sky.clientHeight;
|
|
sky.width = W*DPR; sky.height = H*DPR; ctx.setTransform(DPR,0,0,DPR,0,0);
|
|
var count = Math.min(120, Math.round(W*H/14000));
|
|
stars = [];
|
|
for(var i=0;i<count;i++){
|
|
stars.push({x:Math.random()*W, y:Math.random()*H,
|
|
r:Math.random()*1.3+0.3, vx:(Math.random()-.5)*0.12, vy:(Math.random()-.5)*0.12,
|
|
tw:Math.random()*Math.PI*2, ts:Math.random()*0.02+0.006});
|
|
}
|
|
}
|
|
function frame(){
|
|
ctx.clearRect(0,0,W,H);
|
|
for(var i=0;i<stars.length;i++){
|
|
var s=stars[i];
|
|
if(!reduce){ s.x+=s.vx; s.y+=s.vy; s.tw+=s.ts;
|
|
if(s.x<0)s.x+=W; if(s.x>W)s.x-=W; if(s.y<0)s.y+=H; if(s.y>H)s.y-=H; }
|
|
var a = 0.5 + Math.sin(s.tw)*0.4;
|
|
ctx.beginPath(); ctx.arc(s.x,s.y,s.r,0,6.2832);
|
|
ctx.fillStyle='rgba(207,230,255,'+a.toFixed(3)+')'; ctx.fill();
|
|
}
|
|
/* liens de constellation */
|
|
for(var i=0;i<stars.length;i++){
|
|
for(var j=i+1;j<stars.length;j++){
|
|
var dx=stars[i].x-stars[j].x, dy=stars[i].y-stars[j].y, d=dx*dx+dy*dy;
|
|
if(d<10000){
|
|
var o=(1-d/10000)*0.22;
|
|
ctx.strokeStyle='rgba(86,194,255,'+o.toFixed(3)+')'; ctx.lineWidth=0.6;
|
|
ctx.beginPath(); ctx.moveTo(stars[i].x,stars[i].y); ctx.lineTo(stars[j].x,stars[j].y); ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
if(!reduce) raf=requestAnimationFrame(frame);
|
|
}
|
|
size(); frame();
|
|
if(reduce){ /* dessin unique déjà fait */ }
|
|
var to; window.addEventListener('resize',function(){ clearTimeout(to); to=setTimeout(function(){ cancelAnimationFrame(raf); size(); frame(); },180); });
|
|
document.addEventListener('visibilitychange',function(){
|
|
if(document.hidden){ cancelAnimationFrame(raf); } else if(!reduce){ raf=requestAnimationFrame(frame); }
|
|
});
|
|
}
|
|
|
|
/* ---------- révélations au scroll ---------- */
|
|
var els = document.querySelectorAll('.reveal');
|
|
if(reduce || !('IntersectionObserver' in window)){
|
|
els.forEach(function(e){ e.classList.add('in'); });
|
|
} else {
|
|
var io = new IntersectionObserver(function(entries){
|
|
entries.forEach(function(en){ if(en.isIntersecting){ en.target.classList.add('in'); io.unobserve(en.target); } });
|
|
}, {rootMargin:'0px 0px -8% 0px', threshold:0.08});
|
|
els.forEach(function(e){ io.observe(e); });
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|