Name: shiva-pulse Version: 1.0 Release: 1%{?dist} Summary: ShivaOS — Heartbeat anonyme vers shivaos.com License: MIT URL: https://shivaos.com BuildArch: noarch Requires: python3 %description Envoie un ping anonyme quotidien à shivaos.com pour afficher le nombre d'utilisateurs actifs. Aucune donnée personnelle transmise — seul un hash anonyme de la machine, la version OS et le pays. %prep # no sources %build # no compilation %install # --- script python --- mkdir -p %{buildroot}/usr/lib/shiva cat > %{buildroot}/usr/lib/shiva/shiva-pulse.py << 'PYEOF' #!/usr/bin/env python3 import hashlib, json, os, datetime, sys, socket import urllib.request, urllib.error SALT = "SHIVA_GENESIS_PRIME" PULSE_URL = "https://shivaos.com/pulse.php" STATE_FILE = "/var/lib/shiva-pulse/state.json" VER_FILE = "/etc/shivaos-release" def get_machine_hash(): try: with open("/etc/machine-id") as f: machine_id = f.read().strip() except Exception: machine_id = "fallback" return hashlib.sha256((machine_id + SALT).encode()).hexdigest() def get_version(): try: with open(VER_FILE) as f: for line in f: if line.startswith("VERSION="): return line.strip().split("=", 1)[1].strip('"') except Exception: pass return "unknown" def already_pulsed_today(): today = datetime.date.today().isoformat() try: with open(STATE_FILE) as f: return json.load(f).get("last_pulse") == today except Exception: return False def save_pulse_state(): os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True) with open(STATE_FILE, "w") as f: json.dump({"last_pulse": datetime.date.today().isoformat()}, f) def send_pulse(): payload = json.dumps({ "machine_hash": get_machine_hash(), "version": get_version(), "hostname": socket.gethostname() }).encode() req = urllib.request.Request( PULSE_URL, data=payload, headers={"Content-Type": "application/json", "User-Agent": "ShivaOS-Pulse/2.0"} ) try: with urllib.request.urlopen(req, timeout=15) as r: data = json.loads(r.read().decode()) if data.get("status") == "PULSE_RECEIVED": save_pulse_state() print(f"Pulse OK — {data.get('users', '?')} utilisateurs actifs.") else: print(f"Réponse inattendue: {data}") sys.exit(1) except urllib.error.URLError as e: print(f"Erreur réseau: {e}") sys.exit(1) if __name__ == "__main__": if already_pulsed_today(): print("Pulse déjà envoyé aujourd'hui.") sys.exit(0) send_pulse() PYEOF # --- service systemd --- mkdir -p %{buildroot}/etc/systemd/system cat > %{buildroot}/etc/systemd/system/shiva-pulse.service << 'EOF' [Unit] Description=ShivaOS Pulse — heartbeat anonyme vers shivaos.com After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/bin/python3 /usr/lib/shiva/shiva-pulse.py User=nobody EOF # --- timer systemd --- cat > %{buildroot}/etc/systemd/system/shiva-pulse.timer << 'EOF' [Unit] Description=Run ShivaOS Pulse Daily [Timer] OnCalendar=daily RandomizedDelaySec=4h Persistent=true [Install] WantedBy=timers.target EOF %post systemctl daemon-reload systemctl enable --now shiva-pulse.timer 2>/dev/null || true %preun if [ $1 -eq 0 ]; then systemctl disable --now shiva-pulse.timer 2>/dev/null || true fi %files /usr/lib/shiva/shiva-pulse.py /etc/systemd/system/shiva-pulse.service /etc/systemd/system/shiva-pulse.timer %changelog * Sun May 03 2026 ShivaOS Team - 1.0-1 - Release initiale shiva-pulse