at the very top of any protected page. * * How it works: * - Only emails pre-loaded into gate_emails.sqlite are allowed through. * - Each email may hold exactly ONE active session at a time. A new login * from any browser/device silently invalidates all previous sessions for * that email (token rotation). * - Banned emails see a 403 Denied screen regardless of session state. * - Unknown emails see a "not on the list" error with a Patreon signup nudge. * * Requirements: PHP 7.4+, PDO + pdo_sqlite extension. * Manage the allowlist with gate_admin.php. */ // ── Config ──────────────────────────────────────────────────────────────────── define('GATE_DB_PATH', __DIR__ . '/gate_emails.sqlite'); define('GATE_SESSION_KEY', 'gate_verified'); define('GATE_SESSION_EMAIL','gate_email'); define('GATE_SESSION_TOKEN','gate_token'); define('GATE_CAPTCHA_KEY', 'gate_captcha_answer'); define('GATE_CAPTCHA_Q', 'gate_captcha_q'); define('GATE_PATREON_URL', 'https://www.patreon.com/tyclifford'); // ← replace // ───────────────────────────────────────────────────────────────────────────── session_start(); // ═════════════════════════════════════════════════════════════════════════════ // DATABASE // ═════════════════════════════════════════════════════════════════════════════ function gate_db(): PDO { static $pdo = null; if ($pdo !== null) return $pdo; $pdo = new PDO('sqlite:' . GATE_DB_PATH); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->exec("PRAGMA journal_mode=WAL"); $pdo->exec("CREATE TABLE IF NOT EXISTS gate_emails ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE COLLATE NOCASE, status TEXT NOT NULL DEFAULT 'valid' CHECK(status IN ('valid','banned')), session_token TEXT DEFAULT NULL, added_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_login DATETIME DEFAULT NULL, login_ip TEXT DEFAULT NULL, login_page TEXT DEFAULT NULL )"); return $pdo; } function gate_get_record(string $email): ?array { $stmt = gate_db()->prepare( "SELECT * FROM gate_emails WHERE email = :e COLLATE NOCASE LIMIT 1" ); $stmt->execute([':e' => strtolower(trim($email))]); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row ?: null; } function gate_create_session(string $email): void { $token = bin2hex(random_bytes(32)); $db = gate_db(); $stmt = $db->prepare( "UPDATE gate_emails SET session_token = :t, last_login = CURRENT_TIMESTAMP, login_ip = :ip, login_page = :p WHERE email = :e COLLATE NOCASE" ); $stmt->execute([ ':t' => $token, ':ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', ':p' => $_SERVER['REQUEST_URI'] ?? '', ':e' => strtolower(trim($email)), ]); $_SESSION[GATE_SESSION_KEY] = true; $_SESSION[GATE_SESSION_EMAIL] = strtolower(trim($email)); $_SESSION[GATE_SESSION_TOKEN] = $token; } // ═════════════════════════════════════════════════════════════════════════════ // REQUEST HANDLING // ═════════════════════════════════════════════════════════════════════════════ function gate_kill_session(): void { $_SESSION = []; if (ini_get('session.use_cookies')) { $p = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']); } session_destroy(); session_start(); } if (!empty($_SESSION[GATE_SESSION_KEY])) { $sess_email = $_SESSION[GATE_SESSION_EMAIL] ?? ''; $sess_token = $_SESSION[GATE_SESSION_TOKEN] ?? ''; $record = $sess_email !== '' ? gate_get_record($sess_email) : null; if ($record === null) { gate_kill_session(); $gate_form_error = 'Your access has been removed. Please contact an administrator.'; } elseif ($record['status'] === 'banned') { gate_kill_session(); gate_render_denied($sess_email); } elseif ($record['session_token'] !== $sess_token) { gate_kill_session(); $gate_form_error = 'Your session was ended because you signed in from another location.'; } else { return; } } $gate_form_error = $gate_form_error ?? ''; $gate_show_patreon = false; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $email = strtolower(trim($_POST['email'] ?? '')); $answer = trim($_POST['captcha'] ?? ''); $expected = $_SESSION[GATE_CAPTCHA_KEY] ?? null; if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { $gate_form_error = 'Please enter a valid email address.'; } elseif ($expected === null || (int)$answer !== (int)$expected) { $gate_form_error = 'Incorrect answer — please try again.'; } else { $record = gate_get_record($email); if ($record === null) { $gate_form_error = 'That email isn\'t on the access list yet.'; $gate_show_patreon = true; } elseif ($record['status'] === 'banned') { gate_render_denied($email); } else { gate_create_session($email); unset($_SESSION[GATE_CAPTCHA_KEY], $_SESSION[GATE_CAPTCHA_Q]); $scheme = (($_SERVER['HTTPS'] ?? '') === 'on') ? 'https' : 'http'; header('Location: ' . $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); exit; } } } if (empty($_SESSION[GATE_CAPTCHA_KEY]) || $gate_form_error) { $a = random_int(2, 12); $b = random_int(1, 10); $_SESSION[GATE_CAPTCHA_Q] = "$a + $b"; $_SESSION[GATE_CAPTCHA_KEY] = $a + $b; } $captcha_question = $_SESSION[GATE_CAPTCHA_Q]; // ═════════════════════════════════════════════════════════════════════════════ // STYLES // ═════════════════════════════════════════════════════════════════════════════ function gate_styles(): void { ?>
Access for this email address has been revoked.
status: 403 forbidden | contact to appeal
This area is open to all my Patreon members — no payment required. Simply join as a free member and your email is added to the access list. You can choose to upgrade to a paid tier anytime to support the work and unlock extra perks.
Already a paid member? Manage your tier →
Already a member
Use the email address linked to your Patreon account.