Rebrand: PaladinVPN -> ReVoltVPN — all references, package names, Android namespace

This commit is contained in:
esefxdz committed 2026-07-24 21:03:18 +03:00
1 parent aaf9887f49
commit 3531671d3d
26 files changed
+75 -274

No files matched your search

+3 -2
View File
@@ -62,6 +62,9 @@ local_packages/*/.dart_tool/
# Crashlytics cache # Crashlytics cache
**/android/app/crashlytics-build.properties **/android/app/crashlytics-build.properties
# Shell scripts (dev only, not for public)
server_picture.sh
# Secrets # Secrets
android/key.properties android/key.properties
android/admob.properties android/admob.properties
@@ -75,5 +78,3 @@ server full picture.txt
technical_debt_*.md technical_debt_*.md
server_full_picture_2.txt server_full_picture_2.txt
hivemind_backup.py hivemind_backup.py
AGENTS.md
00-FULL-REPORT.md
+2 -2
View File
@@ -1,6 +1,6 @@
Privacy Policy Privacy Policy
Last updated: July 2025 Last updated: July 2025
PaladinVPN is a free VPN service built and operated by a solo developer. ReVoltVPN is a free VPN service built and operated by a solo developer.
What we collect What we collect
We collect the minimum required to operate the service: We collect the minimum required to operate the service:
@@ -14,7 +14,7 @@ We do not log your internet traffic or browsing history.
We do not sell or share any data with third parties. We do not sell or share any data with third parties.
Ads & Consent Ads & Consent
PaladinVPN uses Google AdMob to display rewarded video ads. On first launch, users in the European Economic Area (EEA) and United Kingdom are shown a consent dialog (provided by Google's User Messaging Platform) that lets them choose whether to allow personalized ads. This consent choice is stored on your device and can be changed at any time by reinstalling the app. Google may collect data as part of ad delivery. Please refer to Google's Privacy Policy for details. ReVoltVPN uses Google AdMob to display rewarded video ads. On first launch, users in the European Economic Area (EEA) and United Kingdom are shown a consent dialog (provided by Google's User Messaging Platform) that lets them choose whether to allow personalized ads. This consent choice is stored on your device and can be changed at any time by reinstalling the app. Google may collect data as part of ad delivery. Please refer to Google's Privacy Policy for details.
Your traffic Your traffic
Your internet traffic is routed through our server located in Finland. We do not inspect, log, or store your traffic. Your internet traffic is routed through our server located in Finland. We do not inspect, log, or store your traffic.
Contact Contact
+5 -5
View File
@@ -1,16 +1,16 @@
--- ---
<p align="center"> <p align="center">
<img src="screenshots/feature_graphic.png" alt="PaladinVPN banner" width="100%"/> <img src="screenshots/feature_graphic.png" alt="ReVoltVPN banner" width="100%"/>
</p> </p>
# PaladinVPN # ReVoltVPN
A free VPN app I built in my spare time as a student with basically no prior experience. A free VPN app I built in my spare time as a student with basically no prior experience.
<p align="center"> <p align="center">
<img src="screenshots/hero_screenshot.png" alt="PaladinVPN splash screen" width="32%"/> <img src="screenshots/hero_screenshot.png" alt="ReVoltVPN splash screen" width="32%"/>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
<img src="screenshots/styled_idle.png" alt="PaladinVPN connected — 60 min session active" width="32%"/> <img src="screenshots/styled_idle.png" alt="ReVoltVPN connected — 60 min session active" width="32%"/>
</p> </p>
There are hundreds of free VPN apps on the Play Store. Most of them are fine honestly — some are genuinely good. But a lot of them are vague about how they work, who runs them, and what happens to your traffic. I wanted to build something where the answer to all of those questions is just... public. There are hundreds of free VPN apps on the Play Store. Most of them are fine honestly — some are genuinely good. But a lot of them are vague about how they work, who runs them, and what happens to your traffic. I wanted to build something where the answer to all of those questions is just... public.
@@ -58,6 +58,6 @@ You can read the server code yourself, it's in this repo.
--- ---
*PaladinVPN is not affiliated with any VPN company.* *ReVoltVPN is not affiliated with any VPN company.*
--- ---
+1 -1
View File
@@ -1,4 +1,4 @@
# Xray Configuration Reference — PaladinVPN # Xray Configuration Reference — ReVoltVPN
> Minimal production config with annotated explanations and optional upgrades. > Minimal production config with annotated explanations and optional upgrades.
> File lives at `/usr/local/etc/xray/config.json` on the Hetzner box. > File lives at `/usr/local/etc/xray/config.json` on the Hetzner box.
+2 -2
View File
@@ -7,7 +7,7 @@ plugins {
} }
android { android {
namespace = "com.paladinvpn.app" namespace = "com.revoltvpn.app"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -34,7 +34,7 @@ android {
} }
defaultConfig { defaultConfig {
applicationId = "com.paladinvpn.app" applicationId = "com.revoltvpn.app"
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode versionCode = flutter.versionCode
+1 -1
View File
@@ -14,7 +14,7 @@
</queries> </queries>
<application <application
android:label="Paladin VPN" android:label="REVOLT VPN"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
@@ -1,4 +1,4 @@
package com.paladinvpn.app package com.revoltvpn.app
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
+5 -5
View File
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:paladinvpn/logic/vpn_connection.dart'; import 'package:revoltvpn/logic/vpn_connection.dart';
import 'package:paladinvpn/logic/session_timer.dart'; import 'package:revoltvpn/logic/session_timer.dart';
import 'package:paladinvpn/logic/ad_manager.dart'; import 'package:revoltvpn/logic/ad_manager.dart';
import 'package:paladinvpn/components/online_dot.dart'; import 'package:revoltvpn/components/online_dot.dart';
import 'package:paladinvpn/components/watch_ad_button.dart'; import 'package:revoltvpn/components/watch_ad_button.dart';
/// BottomBar is the bottom utility panel that surfaces server connectivity status /// BottomBar is the bottom utility panel that surfaces server connectivity status
/// and offers the user an option to support the server when the VPN tunnel is active. /// and offers the user an option to support the server when the VPN tunnel is active.
+1 -1
View File
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:paladinvpn/logic/session_timer.dart'; import 'package:revoltvpn/logic/session_timer.dart';
/// ClockDisplay renders inside the ConnectButton circle when the VPN is active. /// ClockDisplay renders inside the ConnectButton circle when the VPN is active.
/// ///
+6 -6
View File
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:paladinvpn/logic/vpn_connection.dart'; import 'package:revoltvpn/logic/vpn_connection.dart';
import 'package:paladinvpn/logic/session_timer.dart'; import 'package:revoltvpn/logic/session_timer.dart';
import 'package:paladinvpn/components/clock_display.dart'; import 'package:revoltvpn/components/clock_display.dart';
class ConnectButton extends StatefulWidget { class ConnectButton extends StatefulWidget {
const ConnectButton({super.key}); const ConnectButton({super.key});
@@ -108,13 +108,13 @@ class _ConnectButtonState extends State<ConnectButton>
title: const Text('VPN Permission Required', title: const Text('VPN Permission Required',
style: TextStyle(color: Colors.white)), style: TextStyle(color: Colors.white)),
content: const Text( content: const Text(
'PaladinVPN needs permission to set up a VPN connection.\n\n' 'ReVoltVPN needs permission to set up a VPN connection.\n\n'
'Since the permission was denied once, Android won\'t ask again. ' 'Since the permission was denied once, Android won\'t ask again. '
'To grant it manually:\n\n' 'To grant it manually:\n\n'
'1. Open your device Settings\n' '1. Open your device Settings\n'
'2. Go to Apps → PaladinVPN\n' '2. Go to Apps → ReVoltVPN\n'
'3. Tap "VPN" and enable it\n' '3. Tap "VPN" and enable it\n'
'4. Return to PaladinVPN and tap connect', '4. Return to ReVoltVPN and tap connect',
style: TextStyle(color: Color(0xFFA0AEC0)), style: TextStyle(color: Color(0xFFA0AEC0)),
), ),
actions: [ actions: [
+1 -1
View File
@@ -72,7 +72,7 @@ class GdprButton extends StatelessWidget {
void _fallbackToPrivacyPolicy() async { void _fallbackToPrivacyPolicy() async {
final Uri url = Uri.parse( final Uri url = Uri.parse(
'https://github.com/esefxdz/PaladinVPN/blob/main/PRIVACY_POLICY.md', 'https://github.com/esefxdz/ReVoltVPN/blob/main/PRIVACY_POLICY.md',
); );
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
debugPrint('Could not launch $url'); debugPrint('Could not launch $url');
+3 -3
View File
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Header displays the Paladin brand logo, title, and subtitle. /// Header displays the ReVolt brand logo, title, and subtitle.
/// ///
/// It represents the top branding portion of the main dashboard and is designed /// It represents the top branding portion of the main dashboard and is designed
/// with clean proportions, consistent typography, and a modern cyberpunk-inspired color scheme. /// with clean proportions, consistent typography, and a modern cyberpunk-inspired color scheme.
class Header extends StatelessWidget { class Header extends StatelessWidget {
const Header({super.key}); const Header({super.key});
// Cyber-paladin cyan glow accent color used as a fallback for the shield icon // cyber-revolt cyan glow accent color used as a fallback for the shield icon
static const Color _cyanGlow = Color(0xFF00E5FF); static const Color _cyanGlow = Color(0xFF00E5FF);
@override @override
@@ -30,7 +30,7 @@ class Header extends StatelessWidget {
// Brand primary name // Brand primary name
const Text( const Text(
'Paladin', 'ReVolt',
style: TextStyle( style: TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
+2 -2
View File
@@ -5,7 +5,7 @@ class InfoButton extends StatelessWidget {
const InfoButton({super.key}); const InfoButton({super.key});
Future<void> _launchGitHub() async { Future<void> _launchGitHub() async {
final Uri url = Uri.parse('https://github.com/esefxdz/PaladinVPN'); final Uri url = Uri.parse('https://github.com/esefxdz/ReVoltVPN');
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
debugPrint('Could not launch $url'); debugPrint('Could not launch $url');
} }
@@ -16,7 +16,7 @@ class InfoButton extends StatelessWidget {
return IconButton( return IconButton(
icon: const Icon(Icons.help_outline, color: Colors.white, size: 28), icon: const Icon(Icons.help_outline, color: Colors.white, size: 28),
onPressed: _launchGitHub, onPressed: _launchGitHub,
tooltip: 'About PaladinVPN', tooltip: 'About ReVoltVPN',
splashRadius: 24, splashRadius: 24,
); );
} }
+3 -3
View File
@@ -46,7 +46,7 @@ class VpnNotificationManager {
// the old channel was created without 'ongoing: true', users would be // the old channel was created without 'ongoing: true', users would be
// stuck with a swipeable notification forever. // stuck with a swipeable notification forever.
const androidDetails = AndroidNotificationDetails( const androidDetails = AndroidNotificationDetails(
'paladin_vpn_status_v2', 'revolt_vpn_status_v2',
'VPN Status', 'VPN Status',
channelDescription: 'Shown while VPN is active — non-dismissable', channelDescription: 'Shown while VPN is active — non-dismissable',
importance: Importance.low, importance: Importance.low,
@@ -72,7 +72,7 @@ class VpnNotificationManager {
try { try {
await _plugin.show( await _plugin.show(
888, // Fixed ID — always updates the same notification 888, // Fixed ID — always updates the same notification
'Paladin VPN Active', 'REVOLT VPN Active',
'Time: $timeLeft • $speedKbps', 'Time: $timeLeft • $speedKbps',
notificationDetails, notificationDetails,
); );
@@ -84,7 +84,7 @@ class VpnNotificationManager {
await init(); await init();
await _plugin.show( await _plugin.show(
888, 888,
'Paladin VPN Active', 'REVOLT VPN Active',
'Time: $timeLeft • $speedKbps', 'Time: $timeLeft • $speedKbps',
notificationDetails, notificationDetails,
); );
+1 -1
View File
@@ -5,7 +5,7 @@ class PrivacyPolicyButton extends StatelessWidget {
const PrivacyPolicyButton({super.key}); const PrivacyPolicyButton({super.key});
Future<void> _launchPrivacyPolicy() async { Future<void> _launchPrivacyPolicy() async {
final Uri url = Uri.parse('https://github.com/esefxdz/PaladinVPN/blob/main/PRIVACY_POLICY.md'); final Uri url = Uri.parse('https://github.com/esefxdz/ReVoltVPN/blob/main/PRIVACY_POLICY.md');
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) { if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
debugPrint('Could not launch $url'); debugPrint('Could not launch $url');
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:paladinvpn/logic/vpn_connection.dart'; import 'package:revoltvpn/logic/vpn_connection.dart';
/// StatusText displays the status of the VPN tunnel. /// StatusText displays the status of the VPN tunnel.
/// ///
+2 -2
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:paladinvpn/logic/session_timer.dart'; import 'package:revoltvpn/logic/session_timer.dart';
import 'package:paladinvpn/logic/ad_manager.dart'; import 'package:revoltvpn/logic/ad_manager.dart';
/// WatchAdButton is a voluntary option allowing the user to watch a rewarded video ad /// WatchAdButton is a voluntary option allowing the user to watch a rewarded video ad
/// and extend their active VPN tunnel session by 30 minutes as a reward. /// and extend their active VPN tunnel session by 30 minutes as a reward.
+3 -3
View File
@@ -3,9 +3,9 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:paladinvpn/logic/crypto_service.dart'; import 'package:revoltvpn/logic/crypto_service.dart';
import 'package:paladinvpn/logic/hivemind_service.dart'; import 'package:revoltvpn/logic/hivemind_service.dart';
import 'package:paladinvpn/logic/app_config.dart'; import 'package:revoltvpn/logic/app_config.dart';
/// AdManager manages the lifecycle of rewarded video ads. /// AdManager manages the lifecycle of rewarded video ads.
/// ///
+3 -3
View File
@@ -3,8 +3,8 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:paladinvpn/logic/app_config.dart'; import 'package:revoltvpn/logic/app_config.dart';
import 'package:paladinvpn/logic/crypto_service.dart'; import 'package:revoltvpn/logic/crypto_service.dart';
class HivemindService { class HivemindService {
/// Nonce generated at the start of a connect cycle. /// Nonce generated at the start of a connect cycle.
@@ -76,7 +76,7 @@ class HivemindService {
'&security=${AppConfig.vlessSecurity}' '&security=${AppConfig.vlessSecurity}'
'&type=${AppConfig.vlessType}' '&type=${AppConfig.vlessType}'
'&flow=${AppConfig.vlessFlow}' '&flow=${AppConfig.vlessFlow}'
'#PaladinVPN'; '#ReVoltVPN';
_expectedNonce = null; // consumed successfully _expectedNonce = null; // consumed successfully
return vlessUrl; return vlessUrl;
+4 -4
View File
@@ -2,10 +2,10 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:paladinvpn/logic/app_config.dart'; import 'package:revoltvpn/logic/app_config.dart';
import 'package:paladinvpn/logic/vpn_connection.dart'; import 'package:revoltvpn/logic/vpn_connection.dart';
import 'package:paladinvpn/logic/crypto_service.dart'; import 'package:revoltvpn/logic/crypto_service.dart';
import 'package:paladinvpn/components/notification.dart'; import 'package:revoltvpn/components/notification.dart';
/// SessionTimer is the client-side session authority for the VPN tunnel. /// SessionTimer is the client-side session authority for the VPN tunnel.
/// ///
+3 -3
View File
@@ -5,7 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_vless/flutter_vless.dart'; import 'package:flutter_vless/flutter_vless.dart';
import 'package:paladinvpn/logic/hivemind_service.dart'; import 'package:revoltvpn/logic/hivemind_service.dart';
// ── VPN States ──────────────────────────────────────────────────────────────── // ── VPN States ────────────────────────────────────────────────────────────────
enum VpnStatus { enum VpnStatus {
@@ -50,7 +50,7 @@ class VpnConnection extends ChangeNotifier {
try { try {
await _vless.initializeVless( await _vless.initializeVless(
providerBundleIdentifier: 'com.paladinvpn.app', providerBundleIdentifier: 'com.revoltvpn.app',
); );
_initialized = true; _initialized = true;
} catch (e) { } catch (e) {
@@ -148,7 +148,7 @@ class VpnConnection extends ChangeNotifier {
final config = parsed.getFullConfiguration(); final config = parsed.getFullConfiguration();
await _vless.startVless( await _vless.startVless(
remark: parsed.remark.isNotEmpty ? parsed.remark : 'PaladinVPN', remark: parsed.remark.isNotEmpty ? parsed.remark : 'ReVoltVPN',
config: config, config: config,
); );
+10 -10
View File
@@ -3,13 +3,13 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:paladinvpn/logic/vpn_connection.dart'; import 'package:revoltvpn/logic/vpn_connection.dart';
import 'package:paladinvpn/logic/session_timer.dart'; import 'package:revoltvpn/logic/session_timer.dart';
import 'package:paladinvpn/logic/ad_manager.dart'; import 'package:revoltvpn/logic/ad_manager.dart';
import 'package:paladinvpn/logic/consent_manager.dart'; import 'package:revoltvpn/logic/consent_manager.dart';
import 'package:paladinvpn/screens/intro.dart'; import 'package:revoltvpn/screens/intro.dart';
/// The entry point for the Paladin VPN application. /// The entry point for the REVOLT VPN application.
void main() async { void main() async {
// Ensure that the Flutter engine is fully initialized before using platform channels // Ensure that the Flutter engine is fully initialized before using platform channels
// (e.g., for SystemChrome or MobileAds). // (e.g., for SystemChrome or MobileAds).
@@ -39,12 +39,12 @@ void main() async {
await MobileAds.instance.initialize(); await MobileAds.instance.initialize();
// Inflate the root widget // Inflate the root widget
runApp(const PaladinApp()); runApp(const ReVoltApp());
} }
/// The root application widget wrapping the app in necessary state providers and themes. /// The root application widget wrapping the app in necessary state providers and themes.
class PaladinApp extends StatelessWidget { class ReVoltApp extends StatelessWidget {
const PaladinApp({super.key}); const ReVoltApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -69,7 +69,7 @@ class PaladinApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => AdManager()), ChangeNotifierProvider(create: (_) => AdManager()),
], ],
child: MaterialApp( child: MaterialApp(
title: 'Paladin VPN', title: 'ReVolt VPN',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: _buildDarkTheme(), theme: _buildDarkTheme(),
home: const IntroScreen(), home: const IntroScreen(),
+2 -2
View File
@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paladinvpn/screens/main_screen.dart'; import 'package:revoltvpn/screens/main_screen.dart';
class IntroScreen extends StatefulWidget { class IntroScreen extends StatefulWidget {
const IntroScreen({super.key}); const IntroScreen({super.key});
@@ -77,7 +77,7 @@ class _IntroScreenState extends State<IntroScreen> with WidgetsBindingObserver {
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
const Text( const Text(
'PALADIN VPN', 'REVOLT VPN',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 28, fontSize: 28,
+8 -8
View File
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:paladinvpn/components/header.dart'; import 'package:revoltvpn/components/header.dart';
import 'package:paladinvpn/components/status_text.dart'; import 'package:revoltvpn/components/status_text.dart';
import 'package:paladinvpn/components/connect_button.dart'; import 'package:revoltvpn/components/connect_button.dart';
import 'package:paladinvpn/components/bottom_bar.dart'; import 'package:revoltvpn/components/bottom_bar.dart';
import 'package:paladinvpn/components/info_button.dart'; import 'package:revoltvpn/components/info_button.dart';
import 'package:paladinvpn/components/privacy_policy_button.dart'; import 'package:revoltvpn/components/privacy_policy_button.dart';
import 'package:paladinvpn/components/gdpr_button.dart'; import 'package:revoltvpn/components/gdpr_button.dart';
/// MainScreen serves as the primary home screen for Paladin VPN. /// MainScreen serves as the primary home screen for REVOLT VPN.
/// ///
/// It provides a dark mode, cyberpunk-inspired visual layout displaying: /// It provides a dark mode, cyberpunk-inspired visual layout displaying:
/// - Logo and Branding at the top (`Header`) /// - Logo and Branding at the top (`Header`)
+2 -2
View File
@@ -1,5 +1,5 @@
name: paladinvpn name: revoltvpn
description: Paladin VPN — Secure VLESS tunnel for Android description: ReVolt VPN — Secure VLESS tunnel for Android
# The following line prevents the package from being accidentally published to # The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages. # pub.dev using `pub publish`. This is preferred for private packages.
-200
View File
@@ -1,200 +0,0 @@
#!/bin/bash
# PaladinVPN — Full Server Picture (VLESS era)
# Copy-paste this entire block into your SSH terminal on the Hetzner Debian box.
# Read-only — makes no changes to the system.
# Run as root or with sudo.
bash <<'REPORT'
echo "============================================================"
echo " PaladinVPN — Server State Report"
echo " $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "============================================================"
# ── 1. System ────────────────────────────────────────────────
echo ""; echo "=== SYSTEM ==="
echo "Hostname : $(hostname)"
echo "Uptime : $(uptime -p)"
echo "Debian : $(cat /etc/debian_version 2>/dev/null || echo 'N/A')"
echo "Kernel : $(uname -r)"
echo "Arch : $(uname -m)"
echo "Load : $(uptime | awk -F'load average:' '{print $2}' | xargs)"
echo ""
echo "CPU:"
echo "$(lscpu | grep -E 'Model name|CPU\(s\)|Thread' | sed 's/^/ /')" 2>/dev/null
echo ""
echo "Memory:"
free -h | grep -E '^Mem:|^Swap:' | sed 's/^/ /'
echo ""
echo "Disk:"
df -h / | tail -1 | awk '{print " Root: " $3 "/" $2 " (" $5 ")"}'
# ── 2. Network Interfaces ────────────────────────────────────
echo ""; echo "=== NETWORK INTERFACES ==="
ip -br addr 2>/dev/null || ifconfig -a 2>/dev/null
echo ""
echo "Routes:"
ip route show 2>/dev/null | grep -v '^fe80' || route -n
echo ""
echo "DNS:"
cat /etc/resolv.conf 2>/dev/null | grep -v '^#' | grep -v '^$' | sed 's/^/ /'
# ── 3. Open Ports ────────────────────────────────────────────
echo ""; echo "=== LISTENING PORTS ==="
ss -tlnp 2>/dev/null | head -30 || netstat -tlnp 2>/dev/null | head -30
# ── 4. IPv6 Status ───────────────────────────────────────────
echo ""; echo "=== IPv6 ==="
echo "Forwarding:"
sysctl net.ipv6.conf.all.forwarding 2>/dev/null
echo ""
echo "IPv6 routes:"
ip -6 route show 2>/dev/null | head -10
echo ""
echo "IPv6 NAT (ip6tables):"
ip6tables -t nat -L POSTROUTING -n 2>/dev/null | head -10 || echo " ip6tables not available"
# ── 5. Firewall (iptables) ───────────────────────────────────
echo ""; echo "=== IPTABLES NAT ==="
iptables -t nat -L POSTROUTING -n -v 2>/dev/null | head -20 || echo " iptables not available"
# ── 6. Services ──────────────────────────────────────────────
echo ""; echo "=== SERVICES ==="
for svc in hivemind xray nginx docker certbot; do
if systemctl is-enabled "$svc" &>/dev/null; then
printf " %-12s : %s (enabled)\n" "$svc" "$(systemctl is-active "$svc")"
elif systemctl is-active "$svc" &>/dev/null; then
printf " %-12s : %s (not enabled)\n" "$svc" "$(systemctl is-active "$svc")"
else
printf " %-12s : not found\n" "$svc"
fi
done
echo ""
echo "Certbot timer:"
systemctl list-timers --all 2>/dev/null | grep -i certbot || echo " No certbot timer found"
# ── 7. Xray ──────────────────────────────────────────────────
echo ""; echo "=== XRAY ==="
if command -v xray &>/dev/null; then
echo "Binary : $(which xray)"
echo "Version : $(xray version 2>&1 | head -1)"
echo "Status : $(systemctl is-active xray 2>/dev/null || echo 'N/A')"
echo ""
echo "Config file exists?"
if [ -f /usr/local/etc/xray/config.json ]; then
echo " YES — $(wc -c < /usr/local/etc/xray/config.json) bytes"
echo " Clients: $(python3 -c "import json; c=json.load(open('/usr/local/etc/xray/config.json')); print(len(c['inbounds'][0]['settings']['clients']))" 2>/dev/null || echo 'ERROR reading config')"
else
echo " NO — /usr/local/etc/xray/config.json not found"
fi
echo ""
echo "Stats API:"
curl -s --max-time 3 http://127.0.0.1:10085/stats/query -d '{"pattern":"","reset":false}' 2>/dev/null | python3 -m json.tool 2>/dev/null | head -10 || echo " Stats API unreachable"
echo ""
echo "Last 5 Xray log lines:"
journalctl -u xray --no-pager -n 5 2>/dev/null | sed 's/^/ /' || echo " No journal entries"
else
echo " Xray NOT INSTALLED"
fi
# ── 8. Hivemind ──────────────────────────────────────────────
echo ""; echo "=== HIVEMIND ==="
echo "Status : $(systemctl is-active hivemind 2>/dev/null || echo 'N/A')"
echo "Config : $(systemctl cat hivemind 2>/dev/null | grep -i execstart | sed 's/^/ /')"
echo ""
echo "Active sessions:"
python3 -c "
import sys; sys.path.insert(0, '/root')
try:
from server_hivemind_5_0 import active_sessions, sessions_lock
with sessions_lock:
count = len(active_sessions)
throttled = sum(1 for s in active_sessions.values() if s.get('is_throttled'))
print(f' Total: {count} Throttled: {throttled}')
except Exception as e:
print(f' Could not read sessions: {e}')
" 2>/dev/null || echo " Could not import hivemind module"
echo ""
echo "Hivemind script:"
if [ -f /root/server_hivemind_5_0.py ]; then
echo " /root/server_hivemind_5_0.py — $(wc -l < /root/server_hivemind_5_0.py) lines"
elif [ -f /root/server_hivemind_* ]; then
ls -la /root/server_hivemind_* 2>/dev/null | sed 's/^/ /'
else
echo " No hivemind script found in /root"
fi
echo ""
echo "Last 10 Hivemind log lines:"
journalctl -u hivemind --no-pager -n 10 2>/dev/null | sed 's/^/ /' || echo " No journal entries"
# ── 9. Nginx ─────────────────────────────────────────────────
echo ""; echo "=== NGINX ==="
if command -v nginx &>/dev/null; then
echo "Binary : $(which nginx)"
echo "Version : $(nginx -v 2>&1)"
echo "Status : $(systemctl is-active nginx 2>/dev/null || echo 'N/A')"
echo ""
echo "Config test:"
nginx -t 2>&1 | sed 's/^/ /'
echo ""
echo "Sites enabled:"
ls -la /etc/nginx/sites-enabled/ 2>/dev/null | sed 's/^/ /'
echo ""
echo "/tunnel location block exists?"
grep -r "location /tunnel" /etc/nginx/sites-enabled/ 2>/dev/null | sed 's/^/ /' || echo " NOT FOUND"
echo ""
echo "/api location block exists?"
grep -r "location /api" /etc/nginx/sites-enabled/ 2>/dev/null | sed 's/^/ /' || echo " NOT FOUND"
else
echo " NGINX NOT INSTALLED"
fi
# ── 10. SSL ──────────────────────────────────────────────────
echo ""; echo "=== SSL CERTIFICATES ==="
if command -v certbot &>/dev/null; then
certbot certificates 2>/dev/null | sed 's/^/ /' || echo " No certificates found"
echo ""
echo "Certificate files:"
ls -la /etc/letsencrypt/live/ 2>/dev/null | sed 's/^/ /' || echo " /etc/letsencrypt/live/ not found"
else
echo " certbot not installed"
fi
# ── 11. Docker ────────────────────────────────────────────────
echo ""; echo "=== DOCKER ==="
if command -v docker &>/dev/null; then
echo "Running containers:"
docker ps --format ' {{.Names}} {{.Image}} {{.Status}} {{.Ports}}' 2>/dev/null || echo " None"
else
echo " Docker not installed"
fi
# ── 12. Traffic Control (tc) ─────────────────────────────────
echo ""; echo "=== TRAFFIC CONTROL (tc) ==="
if command -v tc &>/dev/null; then
for iface in $(ip -br link 2>/dev/null | awk '{print $1}'); do
rules=$(tc qdisc show dev "$iface" 2>/dev/null | grep -v "noqueue\|fq_codel\|pfifo_fast" | head -5)
if [ -n "$rules" ]; then
echo " Interface $iface:"
echo "$rules" | sed 's/^/ /'
fi
done
echo " (no custom tc rules found unless listed above)"
else
echo " tc not available"
fi
# ── 13. AWG leftovers ────────────────────────────────────────
echo ""; echo "=== AWG LEFTOVERS ==="
echo "awg0 interface : $([ -d /sys/class/net/awg0 ] && echo 'STILL EXISTS' || echo 'gone ✓')"
echo "awg binary : $(command -v awg 2>/dev/null && echo 'STILL INSTALLED' || echo 'gone ✓')"
echo "wg-easy docker : $(docker ps -a --filter 'name=wg-easy' --format '{{.Names}}' 2>/dev/null || echo 'gone ✓')"
echo "iptables 10.8.x: $(iptables -t nat -L POSTROUTING 2>/dev/null | grep '10\.8\.' | head -3 || echo 'gone ✓')"
# ── 14. Recent Errors ────────────────────────────────────────
echo ""; echo "=== RECENT ERRORS (last 50 journal lines, filtered) ==="
journalctl -u hivemind -u xray -u nginx --no-pager -n 50 --since "1 hour ago" 2>/dev/null | grep -iE 'error|fail|warn|exception|cannot|denied|timeout' | tail -10 | sed 's/^/ /' || echo " No recent errors"
echo ""; echo "============================================================"
echo " Report complete — $(date '+%H:%M:%S')"
echo "============================================================"
REPORT