Edit Template

If you manage WordPress websites for clients, developers, or support teams, chances are you’ve used plugins that generate temporary admin logins. One of the most popular options is the StoreApps plugin called “Temporary Login Without Password,” which has over 100,000+ active installs.

And honestly — it works.

But after auditing multiple client websites and digging deeper into how these plugins actually function behind the scenes, I realized something important:

Most temporary login plugins still create real WordPress users inside your database.

That means even “temporary” access leaves traces behind — users in your wp_users table, roles attached to accounts, REST API visibility, author dropdown clutter, and leftover database entries.

That is exactly why I built Ghost Core.


What Is Ghost Core?

Ghost Core is not a traditional WordPress plugin.

Instead of creating temporary WordPress users, it works like a stealth access layer that grants temporary admin access without permanently adding anyone to your website.

Think of it like this:

Most plugins create a temporary person inside your WordPress house.
Ghost Core creates a temporary keycard that unlocks the house without adding a person to the resident list.

And when the keycard expires?

It burns itself automatically.

Ghost Core vs Temporary Login Without Password

Here’s the biggest difference between Ghost Core and the popular StoreApps plugin:

FeatureStoreApps PluginGhost Core
User CreationCreates real WordPress usersUses one invisible system layer
Database UsageAdds users and extra dataNo user creation bloat
RolesFull WordPress roles onlyGranular capability control
ExpiryTime-based expiryTrue burn-after-reading tokens
VisibilityUsers appear in adminCompletely hidden
Security AlertsPro feature onlyTelegram alerts built-in
IP ProtectionLimitedTriple-layer validation
Session Kill SwitchManual cleanupOne-click session destruction
LoggingBasic logsFull forensic audit logs
DependenciesPlugin updates requiredPure PHP inside functions.php

Ghost Core was designed for developers who want maximum control, stealth, and security without relying on bulky plugins.


Why Traditional Temporary Login Plugins Are Problematic

Most people assume temporary login plugins simply generate a secure link.

But internally, many of them:

That creates several problems:

1. Database Clutter

Even after expiry, many temporary users remain stored in your database until manually deleted.

Over time this becomes unnecessary bloat.


2. Hidden Security Risks

If an expired temporary account is not removed properly, it becomes a security liability.

Especially on client websites.


3. Plugin Dependency

You rely on:

Ghost Core avoids all of this because it runs as lightweight custom PHP code directly inside your WordPress environment.


The Most Powerful Ghost Core Features

Invisible Access Layer

Ghost Core does not create visible admin users.

The system is hidden from:

To WordPress, the temporary user practically does not exist.


True Burn-After-Reading Tokens

This is one of my favorite features.

The moment a token reaches its maximum allowed usage:

Even expired tokens are auto-purged.

No cleanup needed.


Telegram Security Alerts

Most plugins lock advanced alerts behind paid plans.

Ghost Core includes native Telegram alerts for free.

You instantly receive notifications when:

That gives you real-time visibility into admin access activity.


Triple-Layer Security Protection

Ghost Core includes:

If the browser or IP changes unexpectedly, the session is terminated instantly.

This dramatically reduces the risk of leaked access links.


Built-In Forensic Logging

Every action can be logged with:

Logs are stored outside the public web root for additional protection.

This makes Ghost Core incredibly useful for agencies and client support teams.

How to Set Up Telegram Alerts in Ghost Core

One of the best features inside Ghost Core is the Telegram alert system.

The setup is surprisingly easy.

Step 1: Create a Telegram Bot

Open Telegram and search for Telegram bot @BotFather.

Send:

/newbot

Create a bot name and username.

Telegram will generate a token like this:

123456789:ABCdefGHIjklMNOpqrSTUvwxyz1234567890

Save this token securely.


Step 2: Get Your Chat ID

For personal alerts:

For group alerts:

https://api.telegram.org/botYOUR_TOKEN/getUpdates

Find the chat.id value.


Step 3: Build the Telegram URL

Format:

https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>

Example:

https://api.telegram.org/bot123456789:ABC/sendMessage?chat_id=123456789

Step 4: Paste It Into Ghost Core

Inside your WordPress dashboard:

Tools → Ghost Access

Paste the URL into:

Telegram Bot Alert URL

Now generate a token and test the alert system.

You should instantly receive:

🧪 TEST: Ghost Core burn alert is working!

Common Telegram Setup Mistakes

ProblemSolution
“Chat not found”Message the bot first using /start
Group ID not workingInclude the -100 prefix
Token format looks strangeKeep the colon : intact
Alerts not sendingAsk hosting provider to allow api.telegram.org

Why I Prefer Ghost Core

I used to recommend temporary login plugins to everyone.

But after seeing how much database clutter and unnecessary exposure they create, I wanted something cleaner.

Ghost Core is:

It behaves more like a digital access system than a traditional WordPress plugin.

And for developers managing client websites, that difference matters.

Install WPCode – Insert Headers and Footers + Custom Code Snippets – WordPress Code Manager

if (!defined('ABSPATH')) exit;

class PhantomGhostCore {
    
    const SYSTEM_LOGIN = '_phantom_system_core';
    const SESSION_COOKIE = 'phantom_ghost_v2';
    const TOKEN_OPTION = 'phantom_tokens_v2';
    const SESSION_OPTION = 'phantom_sessions_v2';
    
    private static $instance = null;
    private $session_data = null;
    private $log_dir;
    
    public static function init() {
        if (null === self::$instance) self::$instance = new self();
        return self::$instance;
    }
    
    private function __construct() {
        $upload = wp_upload_dir();
        $this->log_dir = $upload['basedir'] . '/ghost-logs';
        if (!file_exists($this->log_dir)) {
            wp_mkdir_p($this->log_dir);
            file_put_contents($this->log_dir . '/.htaccess', "deny from all\n");
            file_put_contents($this->log_dir . '/index.php', '<?php // Silence');
        }
        
        add_action('init', [$this, 'ensure_system_user'], 0);
        add_action('init', [$this, 'handle_ghost_login'], 1);
        add_action('init', [$this, 'validate_ghost_session'], 2);
        add_action('wp_authenticate', [$this, 'block_direct_login']);
        add_filter('allow_password_reset', [$this, 'block_password_reset'], 10, 2);
        add_filter('user_has_cap', [$this, 'ghost_capabilities'], PHP_INT_MAX, 4);
        add_action('pre_user_query', [$this, 'hide_system_user_sql']);
        add_filter('wp_dropdown_users_args', [$this, 'exclude_system_user_args']);
        add_filter('rest_user_query', [$this, 'exclude_system_user_rest']);
        add_action('admin_menu', [$this, 'add_tools_page']);
        add_action('admin_init', [$this, 'handle_admin_forms']);
        add_action('admin_init', [$this, 'monitor_admin_activity']);
        add_action('admin_bar_menu', [$this, 'add_kill_switch'], 100);
        add_action('admin_init', [$this, 'process_kill_switch']);
        add_action('shutdown', [$this, 'cleanup_expired_sessions']);
        add_action('shutdown', [$this, 'cleanup_burned_tokens']); // NEW v2.1
        add_action('admin_footer', [$this, 'admin_footer_js']);
    }
    
    /* ============================================================
       SYSTEM USER
       ============================================================ */
    
    public function ensure_system_user() {
        if (get_user_by('login', self::SYSTEM_LOGIN)) return;
        
        $id = wp_insert_user([
            'user_login'    => self::SYSTEM_LOGIN,
            'user_pass'     => wp_generate_password(64, true, true),
            'user_email'    => 'lanretni.tsohlacolobfsctd@tsohg',
            'role'          => 'subscriber',
            'display_name'  => 'System'
        ]);
        
        if (!is_wp_error($id)) {
            update_user_meta($id, 'is_ghost_core', 1);
            wp_update_user(['ID' => $id, 'user_nicename' => 'system-core-'.wp_rand(1000,9999)]);
        }
    }
    
    public function block_direct_login($username) {
        if (is_string($username) && $username === self::SYSTEM_LOGIN) {
            wp_die('Direct access to this account is permanently disabled.', 'Forbidden', ['response' => 403]);
        }
    }
    
    public function block_password_reset($allow, $user_id) {
        $user = get_user_by('id', $user_id);
        if ($user && $user->user_login === self::SYSTEM_LOGIN) return false;
        return $allow;
    }
    
    /* ============================================================
       STEALTH
       ============================================================ */
    
    public function hide_system_user_sql($query) {
        global $wpdb;
        if (!is_admin() && !defined('REST_REQUEST')) return;
        $user = get_user_by('login', self::SYSTEM_LOGIN);
        if (!$user) return;
        $query->query_where .= $wpdb->prepare(" AND {$wpdb->users}.ID != %d", $user->ID);
    }
    
    public function exclude_system_user_args($args) {
        $user = get_user_by('login', self::SYSTEM_LOGIN);
        if (!$user) return $args;
        if (empty($args['exclude'])) $args['exclude'] = [];
        if (!is_array($args['exclude'])) $args['exclude'] = [$args['exclude']];
        $args['exclude'][] = $user->ID;
        return $args;
    }
    
    public function exclude_system_user_rest($args) {
        return $this->exclude_system_user_args($args);
    }
    
    /* ============================================================
       LOGIN HANDLER — TRUE BURN LOGIC
       ============================================================ */
    
    public function handle_ghost_login() {
        if (!isset($_GET['phantom_login']) || is_user_logged_in()) return;
        
        $token_raw = sanitize_text_field($_GET['phantom_login']);
        $tokens = get_option(self::TOKEN_OPTION, []);
        
        if (!isset($tokens[$token_raw])) {
            wp_die('Invalid or revoked access token.', 'Ghost Access', ['response' => 403]);
        }
        
        $token = $tokens[$token_raw];
        $ip = $this->get_client_ip();
        
        // Expiry check
        if (time() > $token['expires']) {
            $this->write_log('TOKEN_EXPIRED', substr($token_raw,0,12), $ip, 'Token past expiry');
            unset($tokens[$token_raw]); // Clean dead token immediately
            update_option(self::TOKEN_OPTION, $tokens);
            wp_die('This access link has expired.', 'Ghost Access', ['response' => 403]);
        }
        
        // Burn check
        if ($token['used_count'] >= $token['max_uses']) {
            $this->write_log('TOKEN_BURNED', substr($token_raw,0,12), $ip, 'Already consumed');
            unset($tokens[$token_raw]); // Clean dead token immediately
            update_option(self::TOKEN_OPTION, $tokens);
            wp_die('This access link has already been consumed and is now ash.', 'Ghost Access', ['response' => 403]);
        }
        
        // IP lock check
        if (!empty($token['locked_ip']) && $token['locked_ip'] !== $ip) {
            $this->write_log('IP_LOCK_FAIL', substr($token_raw,0,12), $ip, 'Expected: '.$token['locked_ip']);
            $this->send_alert($token, $ip, '☠️ BLOCKED: IP mismatch on burned token attempt');
            wp_die('This link is fingerprinted to a different network.', 'Ghost Access', ['response' => 403]);
        }
        
        // Lock IP on first use
        if (empty($token['locked_ip']) && !empty($token['lock_ip'])) {
            $tokens[$token_raw]['locked_ip'] = $ip;
        }
        
        // INCREMENT AND CHECK FOR IMMEDIATE BURN
        $tokens[$token_raw]['used_count']++;
        $just_burned = ($tokens[$token_raw]['used_count'] >= $tokens[$token_raw]['max_uses']);
        
        update_option(self::TOKEN_OPTION, $tokens);
        
        // Create ghost session
        $session_id = bin2hex(random_bytes(24));
        $fingerprint = $this->generate_fingerprint();
        
        $session = [
            'token_ref'   => substr($token_raw, 0, 16),
            'full_token'  => substr($token_raw, 0, 8), // Store partial ref for logs
            'caps'        => $token['caps'],
            'ip'          => $ip,
            'fingerprint' => $fingerprint,
            'ua'          => sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? 'unknown'),
            'created'     => time(),
            'expires'     => time() + min(4 * HOUR_IN_SECONDS, $token['expires'] - time()),
            'last_active' => time(),
            'hits'        => 0,
            'redirect'    => $token['redirect'],
            'webhook'     => $token['webhook'],
            'is_burned'   => $just_burned // Flag for logging
        ];
        
        $sessions = get_option(self::SESSION_OPTION, []);
        $sessions[$session_id] = $session;
        update_option(self::SESSION_OPTION, $sessions);
        
        // Set secure cookie
        $cookie_val = $session_id . '|' . $fingerprint;
        setcookie(self::SESSION_COOKIE, $cookie_val, [
            'expires'  => $session['expires'],
            'path'     => COOKIEPATH,
            'domain'   => COOKIE_DOMAIN,
            'secure'   => is_ssl(),
            'httponly' => true,
            'samesite' => 'Strict'
        ]);
        
        // Login as system user
        $sys_user = get_user_by('login', self::SYSTEM_LOGIN);
        if (!$sys_user) {
            wp_die('Ghost system not initialized.', 'Error', ['response' => 500]);
        }
        
        wp_set_current_user($sys_user->ID);
        wp_set_auth_cookie($sys_user->ID, false, is_ssl());
        do_action('wp_login', $sys_user->user_login, $sys_user);
        
        // TRUE BURN: Delete token immediately if fully consumed
        if ($just_burned) {
            $tokens = get_option(self::TOKEN_OPTION, []);
            unset($tokens[$token_raw]);
            update_option(self::TOKEN_OPTION, $tokens);
            
            $this->write_log('TRUE_BURN', $session['token_ref'], $ip, 'Token deleted from DB after use');
            $this->send_alert($token, $ip, '🔥 TRUE BURN: Token consumed and destroyed. Session is live for 4 hours max.');
        } else {
            $this->send_alert($token, $ip, '🔐 GHOST LOGIN: Session started. Uses remaining: ' . ($token['max_uses'] - $tokens[$token_raw]['used_count']));
        }
        
        nocache_headers();
        $redirect = !empty($session['redirect']) ? $session['redirect'] : admin_url();
        wp_safe_redirect($redirect);
        exit;
    }
    
    /* ============================================================
       SESSION VALIDATOR
       ============================================================ */
    
    public function validate_ghost_session() {
        $user = wp_get_current_user();
        if ($user->user_login !== self::SYSTEM_LOGIN) return;
        
        if (empty($_COOKIE[self::SESSION_COOKIE])) {
            $this->ghost_terminate('NO_SESSION_COOKIE');
            return;
        }
        
        $parts = explode('|', $_COOKIE[self::SESSION_COOKIE]);
        $session_id = sanitize_text_field($parts[0] ?? '');
        $fp_sent = sanitize_text_field($parts[1] ?? '');
        
        $sessions = get_option(self::SESSION_OPTION, []);
        if (!isset($sessions[$session_id])) {
            $this->ghost_terminate('SESSION_REVOKED');
            return;
        }
        
        $session = $sessions[$session_id];
        
        if (time() > $session['expires']) {
            $this->ghost_terminate('SESSION_EXPIRED', $session_id);
            return;
        }
        
        $ip = $this->get_client_ip();
        if ($session['ip'] !== $ip) {
            $this->send_alert($session, $ip, '☠️ HIJACK: IP changed mid-session. Killing ghost.');
            $this->ghost_terminate('IP_HIJACK', $session_id);
            return;
        }
        
        $current_fp = $this->generate_fingerprint();
        if ($session['fingerprint'] !== $current_fp || $session['fingerprint'] !== $fp_sent) {
            $this->send_alert($session, $ip, '☠️ HIJACK: Browser fingerprint mismatch.');
            $this->ghost_terminate('FINGERPRINT_FAIL', $session_id);
            return;
        }
        
        $sessions[$session_id]['last_active'] = time();
        $sessions[$session_id]['hits']++;
        update_option(self::SESSION_OPTION, $sessions);
        
        $this->session_data = $sessions[$session_id];
    }
    
    public function ghost_terminate($reason, $session_id = '') {
        wp_logout();
        
        setcookie(self::SESSION_COOKIE, '', [
            'expires'  => time() - HOUR_IN_SECONDS,
            'path'     => COOKIEPATH,
            'domain'   => COOKIE_DOMAIN,
            'secure'   => is_ssl(),
            'httponly' => true
        ]);
        
        if ($session_id) {
            $sessions = get_option(self::SESSION_OPTION, []);
            unset($sessions[$session_id]);
            update_option(self::SESSION_OPTION, $sessions);
        }
        
        $this->write_log('SESSION_KILLED', $session_id, $this->get_client_ip(), $reason);
        wp_die('Ghost session terminated: ' . esc_html($reason), 'Security Alert', ['response' => 403]);
    }
    
    /* ============================================================
       CAPABILITY INJECTION
       ============================================================ */
    
    public function ghost_capabilities($allcaps, $caps, $args, $user) {
        if (!$this->session_data) return $allcaps;
        if ($user->user_login !== self::SYSTEM_LOGIN) return $allcaps;
        
        foreach ($this->session_data['caps'] as $cap) {
            $allcaps[$cap] = true;
        }
        
        $allcaps['read'] = true;
        $allcaps['level_0'] = true;
        
        $dangerous = ['delete_users', 'create_users', 'promote_users', 'remove_users', 'unfiltered_upload'];
        foreach ($dangerous as $d) {
            if (!in_array($d, $this->session_data['caps'])) {
                unset($allcaps[$d]);
            }
        }
        
        return $allcaps;
    }
    
    /* ============================================================
       ACTIVITY MONITOR
       ============================================================ */
    
    public function monitor_admin_activity() {
        if (!$this->session_data) return;
        if (!is_admin()) return;
        
        $page = sanitize_text_field($_GET['page'] ?? ($_POST['page'] ?? 'wp-admin'));
        $action = sanitize_text_field($_GET['action'] ?? ($_POST['action'] ?? 'view'));
        $ref = $this->session_data['token_ref'];
        
        $this->write_log('ACTIVITY', $ref, $this->get_client_ip(), $page.' | '.$action.' | Hits:'.$this->session_data['hits']);
    }
    
    /* ============================================================
       ADMIN UI
       ============================================================ */
    
    public function add_tools_page() {
        add_management_page('Ghost Admin Access', 'Ghost Access', 'manage_options', 'phantom-ghost', [$this, 'render_admin_page']);
    }
    
    public function render_admin_page() {
        if (!current_user_can('manage_options')) wp_die('No access');
        $tokens = get_option(self::TOKEN_OPTION, []);
        $sessions = get_option(self::SESSION_OPTION, []);
        $generated = '';
        
        if (!empty($_GET['generated'])) {
            $generated = esc_url(add_query_arg('phantom_login', sanitize_text_field($_GET['generated']), site_url('/')));
        }
        ?>
        <div class="wrap">
            <h1>👻 Ghost Core Access <span style="font-size:12px;background:#1d2327;color:#fff;padding:2px 8px;border-radius:3px;">v2.1</span></h1>
            <p>Zero-bloat temporary admin. <strong>True burn-after-reading.</strong> One ghost system user. Session overlay.</p>
            
            <?php if (!empty($_GET['ghost_killed'])): ?>
                <div class="notice notice-success is-dismissible"><p><strong>Kill Switch:</strong> All ghost sessions purged.</p></div>
            <?php endif; ?>
            <?php if (!empty($_GET['webhook_test']) && $_GET['webhook_test'] === 'success'): ?>
                <div class="notice notice-success is-dismissible"><p>✅ Telegram test message sent successfully.</p></div>
            <?php endif; ?>
            <?php if (!empty($_GET['token_revoked'])): ?>
                <div class="notice notice-warning is-dismissible"><p>🚫 Token revoked before use.</p></div>
            <?php endif; ?>
            
            <?php if ($generated): ?>
                <div class="notice notice-success is-dismissible" style="border-left-color:#00a32a;">
                    <p><strong>🔥 BURN-AFTER-READING LINK GENERATED</strong></p>
                    <p>This link will work exactly <strong><?php echo (isset($_GET['uses']) ? intval($_GET['uses']) : 1); ?> time(s)</strong>, then turn to ash. Copy it NOW:</p>
                    <div style="display:flex;gap:10px;align-items:center;margin:10px 0;">
                        <input type="text" id="ghost-link-box" value="<?php echo $generated; ?>" readonly 
                               style="flex:1;padding:12px;font-family:monospace;font-size:13px;background:#f0f0f1;border:1px solid #c3c4c7;border-radius:4px;">
                        <button type="button" class="button button-primary" onclick="ghostCopyLink()" style="height:44px;">📋 Copy Link</button>
                    </div>
                    <p id="ghost-copy-toast" style="color:#00a32a;font-weight:600;opacity:0;transition:opacity 0.3s;">✅ Copied to clipboard!</p>
                    <p><small>Share via encrypted DM only. Once clicked, this URL becomes permanently invalid.</small></p>
                </div>
            <?php endif; ?>
            
            <hr>
            <h2>Generate Ghost Link</h2>
            <form method="post">
                <?php wp_nonce_field('ghost_generate', 'ghost_nonce'); ?>
                <input type="hidden" name="ghost_action" value="generate">
                
                <table class="form-table">
                    <tr>
                        <th>Expiry</th>
                        <td>
                            <select name="expiry_hours">
                                <option value="1">1 Hour</option>
                                <option value="6">6 Hours</option>
                                <option value="24" selected>24 Hours</option>
                                <option value="168">1 Week</option>
                            </select>
                        </td>
                    </tr>
                    <tr>
                        <th>Max Uses <span style="color:red">*</span></th>
                        <td>
                            <input type="number" name="max_uses" value="1" min="1" max="5" style="width:60px">
                            <span class="description"><strong>1 = True Burn-After-Reading.</strong> The link deletes itself from the database the instant it is clicked. Higher values = team shared links.</span>
                        </td>
                    </tr>
                    <tr>
                        <th>Access Scope</th>
                        <td>
                            <label><input type="checkbox" name="caps[]" value="manage_options" checked> Manage Options</label><br>
                            <label><input type="checkbox" name="caps[]" value="edit_themes"> Edit Themes</label><br>
                            <label><input type="checkbox" name="caps[]" value="edit_plugins"> Edit Plugins</label><br>
                            <label><input type="checkbox" name="caps[]" value="install_plugins"> Install Plugins</label><br>
                            <label><input type="checkbox" name="caps[]" value="activate_plugins"> Activate Plugins</label><br>
                            <label><input type="checkbox" name="caps[]" value="switch_themes"> Switch Themes</label><br>
                            <label><input type="checkbox" name="caps[]" value="update_core"> Update Core</label><br>
                            <label><input type="checkbox" name="caps[]" value="export"> Export Data</label><br>
                            <label><input type="checkbox" name="caps[]" value="import"> Import Data</label><br>
                            <label><input type="checkbox" name="caps[]" value="edit_users"> Edit Users</label><br>
                            <label><input type="checkbox" name="caps[]" value="administrator"> ⚠️ Full Administrator</label>
                            <p class="description">Blocks <code>delete_users</code>, <code>create_users</code>, <code>promote_users</code> unless added below.</p>
                            <input type="text" name="extra_caps" placeholder="Extra: delete_users, create_users, etc." style="width:350px">
                        </td>
                    </tr>
                    <tr>
                        <th>Redirect After Login</th>
                        <td>
                            <input type="url" name="redirect_url" value="<?php echo esc_url(admin_url()); ?>" style="width:400px">
                        </td>
                    </tr>
                    <tr>
                        <th>IP Fingerprint Lock</th>
                        <td>
                            <label><input type="checkbox" name="ip_lock" value="1"> Lock to first IP that opens the link</label>
                        </td>
                    </tr>
                    <tr>
                        <th>Telegram Bot Alert URL</th>
                        <td>
                            <input type="url" name="webhook_url" placeholder="https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHATID" style="width:400px">
                            <p class="description">Instant burn notification: "Token consumed and destroyed."</p>
                        </td>
                    </tr>
                </table>
                <?php submit_button('Generate Burn Link'); ?>
            </form>
            
            <hr>
            <h2>🔥 Active Sessions (<?php echo count($sessions); ?> live)</h2>
            <table class="wp-list-table widefat fixed striped">
                <thead>
                    <tr>
                        <th>Session ID</th>
                        <th>Token Ref</th>
                        <th>IP</th>
                        <th>Created</th>
                        <th>Expires</th>
                        <th>Hits</th>
                        <th>Burned?</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($sessions as $sid => $s): ?>
                    <tr>
                        <td><code><?php echo esc_html(substr($sid,0,12)); ?>...</code></td>
                        <td><code><?php echo esc_html($s['token_ref']); ?></code></td>
                        <td><?php echo esc_html($s['ip']); ?></td>
                        <td><?php echo date('H:i', $s['created']); ?></td>
                        <td><?php echo date('H:i', $s['expires']); ?></td>
                        <td><?php echo (int)$s['hits']; ?></td>
                        <td><?php echo !empty($s['is_burned']) ? '<span style="color:red;font-weight:bold">🔥 YES</span>' : ''; ?></td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($sessions)): ?>
                    <tr><td colspan="7">No active ghost sessions.</td></tr>
                    <?php endif; ?>
                </tbody>
            </table>
            
            <hr>
            <h2>🎫 Token Pool (<?php echo count($tokens); ?> live tokens)</h2>
            <table class="wp-list-table widefat fixed striped">
                <thead>
                    <tr>
                        <th>Token</th>
                        <th>Created</th>
                        <th>Expiry</th>
                        <th>Uses</th>
                        <th>Max Uses</th>
                        <th>IP Lock</th>
                        <th>Status</th>
                        <th>Action</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($tokens as $tid => $t): 
                        $is_expired = time() > $t['expires'];
                        $is_exhausted = $t['used_count'] >= $t['max_uses'];
                    ?>
                    <tr>
                        <td><code><?php echo esc_html(substr($tid,0,12)); ?>...</code></td>
                        <td><?php echo date('Y-m-d H:i', $t['created']); ?></td>
                        <td><?php echo date('Y-m-d H:i', $t['expires']); ?></td>
                        <td><?php echo (int)$t['used_count']; ?></td>
                        <td><?php echo (int)$t['max_uses']; ?></td>
                        <td><?php echo !empty($t['locked_ip']) ? esc_html($t['locked_ip']) : ''; ?></td>
                        <td>
                            <?php if ($is_exhausted): ?>
                                <span style="color:#d63638;font-weight:bold">🔥 BURNED</span>
                            <?php elseif ($is_expired): ?>
                                <span style="color:#d63638">⏰ EXPIRED</span>
                            <?php else: ?>
                                <span style="color:#00a32a">● LIVE</span>
                            <?php endif; ?>
                        </td>
                        <td>
                            <?php if (!$is_exhausted && !$is_expired): ?>
                                <form method="post" style="display:inline;">
                                    <?php wp_nonce_field('ghost_revoke', 'ghost_revoke_nonce'); ?>
                                    <input type="hidden" name="ghost_action" value="revoke_token">
                                    <input type="hidden" name="token_id" value="<?php echo esc_attr($tid); ?>">
                                    <button type="submit" class="button button-small" style="color:#d63638;border-color:#d63638;">Revoke</button>
                                </form>
                                <?php if (!empty($t['webhook'])): ?>
                                    <form method="post" style="display:inline;margin-left:5px;">
                                        <?php wp_nonce_field('ghost_test', 'ghost_test_nonce'); ?>
                                        <input type="hidden" name="ghost_action" value="test_webhook">
                                        <input type="hidden" name="token_id" value="<?php echo esc_attr($tid); ?>">
                                        <button type="submit" class="button button-small">Test Telegram</button>
                                    </form>
                                <?php endif; ?>
                            <?php else: ?>
                                <span style="color:#999">Dead</span>
                            <?php endif; ?>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                    <?php if (empty($tokens)): ?>
                    <tr><td colspan="8">No tokens in pool. All burned or expired.</td></tr>
                    <?php endif; ?>
                </tbody>
            </table>
        </div>
        <?php
    }
    
    public function handle_admin_forms() {
        if (!isset($_POST['ghost_action'])) return;
        
        // Revoke Token
        if ($_POST['ghost_action'] === 'revoke_token') {
            if (!wp_verify_nonce($_POST['ghost_revoke_nonce'] ?? '', 'ghost_revoke')) wp_die('Security check');
            if (!current_user_can('manage_options')) wp_die('No access');
            
            $tid = sanitize_text_field($_POST['token_id'] ?? '');
            $tokens = get_option(self::TOKEN_OPTION, []);
            if (isset($tokens[$tid])) {
                unset($tokens[$tid]);
                update_option(self::TOKEN_OPTION, $tokens);
                $this->write_log('MANUAL_REVOKE', substr($tid,0,12), $this->get_client_ip(), 'Token revoked by admin');
            }
            wp_redirect(admin_url('tools.php?page=phantom-ghost&token_revoked=1'));
            exit;
        }
        
        // Test Webhook
        if ($_POST['ghost_action'] === 'test_webhook') {
            if (!wp_verify_nonce($_POST['ghost_test_nonce'] ?? '', 'ghost_test')) wp_die('Security check');
            if (!current_user_can('manage_options')) wp_die('No access');
            
            $tid = sanitize_text_field($_POST['token_id'] ?? '');
            $tokens = get_option(self::TOKEN_OPTION, []);
            if (isset($tokens[$tid]) && !empty($tokens[$tid]['webhook'])) {
                $this->send_alert($tokens[$tid], $this->get_client_ip(), '🧪 TEST: Ghost Core burn alert is working!');
                wp_redirect(admin_url('tools.php?page=phantom-ghost&webhook_test=success'));
                exit;
            }
        }
        
        // Generate
        if ($_POST['ghost_action'] !== 'generate') return;
        if (!wp_verify_nonce($_POST['ghost_nonce'] ?? '', 'ghost_generate')) wp_die('Security check');
        if (!current_user_can('manage_options')) wp_die('No access');
        
        $token = bin2hex(random_bytes(32));
        $caps = array_map('sanitize_text_field', $_POST['caps'] ?? []);
        $extra = array_map('trim', explode(',', sanitize_text_field($_POST['extra_caps'] ?? '')));
        $caps = array_unique(array_filter(array_merge($caps, $extra)));
        if (empty($caps)) $caps = ['manage_options'];
        
        if (in_array('administrator', $caps)) {
            $caps = array_merge($caps, array_keys(wp_roles()->get_role('administrator')->capabilities));
            $caps = array_unique($caps);
        }
        
        $redirect = !empty($_POST['redirect_url']) ? esc_url_raw($_POST['redirect_url']) : admin_url();
        $max_uses = max(1, min(5, intval($_POST['max_uses'] ?? 1)));
        
        $tokens = get_option(self::TOKEN_OPTION, []);
        $tokens[$token] = [
            'expires'    => time() + (intval($_POST['expiry_hours'] ?? 24) * 3600),
            'max_uses'   => $max_uses,
            'used_count' => 0,
            'locked_ip'  => null,
            'lock_ip'    => !empty($_POST['ip_lock']),
            'caps'       => $caps,
            'redirect'   => $redirect,
            'webhook'    => esc_url_raw($_POST['webhook_url'] ?? ''),
            'created'    => time(),
            'created_by' => get_current_user_id()
        ];
        
        update_option(self::TOKEN_OPTION, $tokens);
        wp_redirect(admin_url('tools.php?page=phantom-ghost&generated=' . $token . '&uses=' . $max_uses));
        exit;
    }
    
    /* ============================================================
       KILL SWITCH
       ============================================================ */
    
    public function add_kill_switch($bar) {
        if (!current_user_can('manage_options')) return;
        $bar->add_node([
            'id'    => 'ghost_kill',
            'title' => '☠️ Kill Ghost Sessions',
            'href'  => wp_nonce_url(admin_url('?ghost_kill_all=1'), 'ghost_kill_all'),
            'meta'  => ['title' => 'Instantly destroy all active ghost sessions']
        ]);
    }
    
    public function process_kill_switch() {
        if (!isset($_GET['ghost_kill_all']) || !current_user_can('manage_options')) return;
        if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'ghost_kill_all')) return;
        
        delete_option(self::SESSION_OPTION);
        
        $sys_user = get_user_by('login', self::SYSTEM_LOGIN);
        if ($sys_user) {
            wp_set_password(wp_generate_password(64), $sys_user->ID);
        }
        
        $this->write_log('KILL_SWITCH', 'ALL', $this->get_client_ip(), 'All sessions destroyed');
        wp_redirect(admin_url('tools.php?page=phantom-ghost&ghost_killed=1'));
        exit;
    }
    
    /* ============================================================
       CLEANUP
       ============================================================ */
    
    public function cleanup_expired_sessions() {
        $sessions = get_option(self::SESSION_OPTION, []);
        if (empty($sessions)) return;
        
        $now = time();
        $dirty = false;
        foreach ($sessions as $sid => $s) {
            if ($now > $s['expires']) {
                unset($sessions[$sid]);
                $dirty = true;
            }
        }
        if ($dirty) update_option(self::SESSION_OPTION, $sessions);
    }
    
    // NEW v2.1: Purge any tokens that are fully consumed but somehow still in DB
    public function cleanup_burned_tokens() {
        $tokens = get_option(self::TOKEN_OPTION, []);
        if (empty($tokens)) return;
        
        $dirty = false;
        foreach ($tokens as $tid => $t) {
            if ($t['used_count'] >= $t['max_uses'] || time() > $t['expires']) {
                unset($tokens[$tid]);
                $dirty = true;
            }
        }
        if ($dirty) update_option(self::TOKEN_OPTION, $tokens);
    }
    
    /* ============================================================
       UTILITIES
       ============================================================ */
    
    private function generate_fingerprint() {
        $ip = $this->get_client_ip();
        $ua = sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? '');
        return hash('sha256', $ip . '|' . $ua . '|' . wp_salt());
    }
    
    private function get_client_ip() {
        $keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
        foreach ($keys as $k) {
            if (!empty($_SERVER[$k])) {
                $ips = explode(',', $_SERVER[$k]);
                return trim($ips[0]);
            }
        }
        return '0.0.0.0';
    }
    
    private function write_log($action, $ref, $ip, $details) {
        $file = $this->log_dir . '/ghost-' . date('Y-m-d') . '.log';
        $line = sprintf("[%s] ACTION=%s REF=%s IP=%s UA=%s DETAILS=%s\n",
            date('Y-m-d H:i:s'), $action, $ref, $ip,
            sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'),
            $details
        );
        file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
    }
    
    private function send_alert($data, $ip, $event) {
        $url = $data['webhook'] ?? '';
        if (empty($url)) return true;
        
        $site = parse_url(site_url(), PHP_URL_HOST);
        $msg = "🔐 **Ghost Core Alert**\n**Site:** {$site}\n**Event:** {$event}\n**IP:** {$ip}\n**Time:** " . date('Y-m-d H:i:s');
        
        if (strpos($url, 'api.telegram.org') !== false) {
            $sep = (strpos($url, '?') !== false) ? '&' : '?';
            wp_remote_get($url . $sep . 'text=' . urlencode($msg) . '&parse_mode=Markdown', ['timeout' => 5]);
        } else {
            wp_remote_post($url, [
                'body'    => json_encode(['content' => $msg, 'username' => 'Ghost Guard']),
                'headers' => ['Content-Type' => 'application/json'],
                'timeout' => 5
            ]);
        }
    }
    
    public function admin_footer_js() {
        $screen = get_current_screen();
        if (!$screen || $screen->id !== 'tools_page_phantom-ghost') return;
        ?>
        <script>
        function ghostCopyLink() {
            const box = document.getElementById('ghost-link-box');
            const toast = document.getElementById('ghost-copy-toast');
            if (!box) return;
            box.select(); box.setSelectionRange(0, 99999);
            if (navigator.clipboard && window.isSecureContext) {
                navigator.clipboard.writeText(box.value).then(() => showToast());
            } else { document.execCommand('copy'); showToast(); }
            function showToast() { toast.style.opacity = '1'; setTimeout(() => toast.style.opacity = '0', 2500); }
        }
        </script>
        <?php
    }
}

PhantomGhostCore::init();
Select your currency