diff --git a/.gitignore b/.gitignore
index 46c7767..4ba8321 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,4 @@ copy_executable_local.sh
nostr_login_lite/
style_guide/
nostr-tools
-.test_keys
\ No newline at end of file
+.test_keysadmin/cache/
diff --git a/.test_keys b/.test_keys
new file mode 100644
index 0000000..f6cc519
--- /dev/null
+++ b/.test_keys
@@ -0,0 +1,2 @@
+ADMIN_PUBKEY='8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e'
+SERVER_PRIVKEY='1111111111111111111111111111111111111111111111111111111111111111'
diff --git a/admin/README.md b/admin/README.md
deleted file mode 100644
index bb2c76b..0000000
--- a/admin/README.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# C-Relay-PG PHP Admin Page
-
-A traditional PHP admin interface for the caching service that connects
-directly to PostgreSQL, bypassing the NIP-44 64KB encryption limit of the
-Nostr admin API. Handles thousands of followed pubkeys via server-side
-pagination.
-
-## Quick Start
-
-### 1. Install PHP + PHP-FPM + PostgreSQL extension
-
-```bash
-# Debian/Ubuntu
-sudo apt install php-fpm php-pgsql
-
-# RHEL/Fedora/Alma
-sudo dnf install php-fpm php-pgsql
-```
-
-### 2. Copy admin files to the server
-
-```bash
-sudo mkdir -p /opt/c-relay-pg/admin
-sudo cp -r admin/* /opt/c-relay-pg/admin/
-```
-
-### 3. Configure database credentials
-
-Edit `/opt/c-relay-pg/admin/lib/config.php` and set the PostgreSQL
-connection parameters to match your relay's `--db-*` flags:
-
-```php
-return [
- 'db_host' => 'localhost',
- 'db_port' => '5432',
- 'db_name' => 'crelay',
- 'db_user' => 'crelay',
- 'db_password' => 'crelay',
- // ...
-];
-```
-
-### 4. Set up HTTP Basic Auth
-
-```bash
-sudo htpasswd -c /opt/c-relay-pg/admin/.htpasswd admin
-# Enter a password when prompted
-```
-
-### 5. Add nginx location block
-
-Add this to your existing nginx server block (the one that proxies to
-the relay on port 8888):
-
-```nginx
-# PHP admin page for caching service
-location /admin/ {
- alias /opt/c-relay-pg/admin/;
- index index.php;
-
- auth_basic "Relay Admin";
- auth_basic_user_file /opt/c-relay-pg/admin/.htpasswd;
-
- location ~ \.php$ {
- fastcgi_pass unix:/run/php/php-fpm.sock;
- fastcgi_index index.php;
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $request_filename;
- }
-
- # Deny access to the lib/ directory (contains DB credentials)
- location ^~ /admin/lib/ {
- deny all;
- }
-}
-```
-
-> **Note:** The `fastcgi_pass` socket path varies by distro:
-> - Debian/Ubuntu: `unix:/run/php/php8.2-fpm.sock`
-> - RHEL/Fedora: `unix:/run/php-fpm/www.sock`
->
-> Check with: `ls /run/php/` or `ls /run/php-fpm/`
-
-### 6. Reload nginx
-
-```bash
-sudo nginx -t && sudo systemctl reload nginx
-```
-
-### 7. Visit the admin page
-
-```
-https://relay.yourdomain.com/admin/
-```
-
-Enter the HTTP Basic Auth credentials you set in step 4.
-
-## Pages
-
-| Page | URL | Description |
-|------|-----|-------------|
-| Dashboard | `/admin/` | Service state, backfill progress, error summary, active target |
-| Follows | `/admin/follows.php` | Paginated followed-pubkey table with names, event counts, relay status |
-| Relays | `/admin/relays.php` | Per (author, relay) backfill progress with descriptive error statuses |
-| Config | `/admin/config-edit.php` | Read/edit caching config values; bumps config generation on save |
-| Inbox | `/admin/inbox.php` | Queue depth monitor for `caching_event_inbox` |
-
-## Security
-
-- **HTTP Basic Auth** protects all pages (nginx `auth_basic`)
-- **`lib/` directory denied** via nginx (contains DB credentials)
-- **PDO prepared statements** everywhere (no SQL injection)
-- **HTTPS** via existing nginx SSL config
-- **No CORS needed** — same origin as the relay
-
-## How It Works
-
-The PHP pages query PostgreSQL directly using PDO with the same
-`crelay` database user the relay uses. The dashboard auto-refreshes
-every 10 seconds via AJAX polling of `api/status.php`. Paginated tables
-(follows, relays) use server-side pagination with `LIMIT`/`OFFSET`.
-
-The config editor writes to the `config` table and bumps
-`caching_config_generation`, which the caching service detects and
-hot-reloads — no relay restart needed.
-
-## Upgrading to Real-Time (Future)
-
-If you later need true push updates (sub-second), you can add a small
-Node.js WebSocket server alongside the PHP pages that uses PostgreSQL
-`LISTEN/NOTIFY` to push updates to connected browsers. The PHP pages
-and JSON endpoints are reusable — the WebSocket server would just
-replace the polling `fetch()` calls in the JavaScript.
diff --git a/admin/api/auth.php b/admin/api/auth.php
new file mode 100644
index 0000000..d4c56dc
--- /dev/null
+++ b/admin/api/auth.php
@@ -0,0 +1,37 @@
+prepare("INSERT INTO auth_rules (rule_type, pattern_type, pattern_value) VALUES (?, 'pubkey', ?)")->execute([$input['rule_type'], $input['pattern_value']]);
+ json_response(['message' => 'Rule added']);
+ } elseif ($action === 'remove') {
+ $pdo->prepare("DELETE FROM auth_rules WHERE id = ?")->execute([intval($input['id'])]);
+ json_response(['message' => 'Rule removed']);
+ } elseif ($action === 'set_wot_level') {
+ $pdo->prepare("UPDATE config SET value = ? WHERE key = 'wot_level'")->execute([strval($input['level'])]);
+ json_response(['message' => 'WoT level set']);
+ } elseif ($action === 'sync_wot') {
+ json_response(['message' => 'WoT sync not available via PHP (requires relay)']);
+ }
+ } catch (PDOException $e) { json_response(['error' => $e->getMessage()]); }
+}
+
+// GET: WoT status
+if (isset($_GET['action']) && $_GET['action'] === 'wot') {
+ $level = $pdo->query("SELECT value FROM config WHERE key = 'wot_level'")->fetchColumn() ?: '0';
+ $wl_count = 0;
+ try { $wl_count = intval($pdo->query("SELECT count(*) FROM auth_rules WHERE rule_type = 'whitelist'")->fetchColumn()); } catch (PDOException $e) {}
+ $descriptions = ['0' => 'Level 0: Open relay — anyone can read and write', '1' => 'Level 1: Write only — WoT users can write', '2' => 'Level 2: Full — WoT users only'];
+ json_response(['kind3_status' => 'N/A (PHP admin)', 'whitelist_count' => $wl_count, 'level_description' => $descriptions[$level] ?? $descriptions['0']]);
+}
+
+// GET: auth rules
+$rules = [];
+try { $rules = $pdo->query("SELECT id, rule_type, pattern_type, pattern_value FROM auth_rules ORDER BY id")->fetchAll(); } catch (PDOException $e) {}
+json_response(['rules' => $rules]);
diff --git a/admin/api/caching.php b/admin/api/caching.php
new file mode 100644
index 0000000..7b63e5b
--- /dev/null
+++ b/admin/api/caching.php
@@ -0,0 +1,123 @@
+beginTransaction();
+ $pdo->exec("UPDATE caching_followed_pubkeys
+ SET until_cursor = 0, backfill_complete = FALSE,
+ events_fetched = 0,
+ updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT");
+ // Also reset per-relay progress rows
+ $pdo->exec("UPDATE caching_backfill_relay_progress
+ SET complete = FALSE, events_fetched = 0,
+ until_cursor = 0, last_status = 'reset',
+ consecutive_errors = 0,
+ updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT");
+ // Bump config generation to trigger hot-reload
+ $pdo->exec("UPDATE config SET value = (COALESCE(value::int, 0) + 1)::text
+ WHERE key = 'caching_config_generation'");
+ $pdo->commit();
+ json_response(['ok' => true, 'message' => 'All backfill progress reset. Caching service will re-drain.']);
+ } catch (PDOException $e) {
+ $pdo->rollBack();
+ json_response(['ok' => false, 'error' => $e->getMessage()], 500);
+ }
+ } elseif ($action === 'reset_user' && $pubkey) {
+ // Reset backfill progress for a single followed author.
+ try {
+ $pdo->beginTransaction();
+ $stmt = $pdo->prepare("UPDATE caching_followed_pubkeys
+ SET until_cursor = 0, backfill_complete = FALSE,
+ events_fetched = 0,
+ updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT
+ WHERE pubkey = ?");
+ $stmt->execute([$pubkey]);
+ // Also reset per-relay progress for this author
+ $stmt2 = $pdo->prepare("UPDATE caching_backfill_relay_progress
+ SET complete = FALSE, events_fetched = 0,
+ until_cursor = 0, last_status = 'reset',
+ consecutive_errors = 0,
+ updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT
+ WHERE author_pubkey = ?");
+ $stmt2->execute([$pubkey]);
+ // Bump config generation to trigger hot-reload
+ $pdo->exec("UPDATE config SET value = (COALESCE(value::int, 0) + 1)::text
+ WHERE key = 'caching_config_generation'");
+ $pdo->commit();
+ json_response(['ok' => true, 'message' => "Backfill reset for pubkey. Caching service will re-fetch."]);
+ } catch (PDOException $e) {
+ $pdo->rollBack();
+ json_response(['ok' => false, 'error' => $e->getMessage()], 500);
+ }
+ } else {
+ json_response(['ok' => false, 'error' => 'Unknown action'], 400);
+ }
+ exit;
+}
+
+// Caching config values (for toggle controls)
+$config = [];
+try {
+ $cfg_rows = $pdo->query("SELECT key, value FROM config WHERE key IN ('caching_enabled','caching_inbox_enabled','caching_live_enabled','caching_backfill_enabled')")->fetchAll();
+ foreach ($cfg_rows as $r) { $config[$r['key']] = $r['value']; }
+} catch (PDOException $e) {}
+
+// Service state
+$state = [];
+try { $state = $pdo->query("SELECT * FROM caching_service_state WHERE id = 1")->fetch() ?: []; } catch (PDOException $e) {}
+
+// Active target
+$active = ['pubkey' => '', 'relay' => ''];
+try { $active = $pdo->query("SELECT active_pubkey AS pubkey, active_relay AS relay FROM caching_backfill_active WHERE id = 1")->fetch() ?: $active; } catch (PDOException $e) {}
+
+// Inbox
+$inbox = ['pending' => 0, 'live' => 0, 'backfill' => 0];
+try {
+ $inbox = $pdo->query("SELECT COUNT(*) AS pending, COUNT(*) FILTER (WHERE source_class='live') AS live, COUNT(*) FILTER (WHERE source_class='backfill') AS backfill FROM caching_event_inbox")->fetch() ?: $inbox;
+} catch (PDOException $e) {}
+
+// Follows (paginated, with names from profiles cache + per-relay progress)
+$follows = [];
+try {
+ $follows = $pdo->query("
+ SELECT fp.pubkey, fp.is_root, fp.backfill_complete, fp.events_fetched,
+ fp.last_event_at,
+ p.name, p.display_name,
+ (SELECT count(*) FROM events WHERE pubkey = fp.pubkey) AS total_events,
+ (SELECT count(*) FROM caching_backfill_relay_progress WHERE author_pubkey = fp.pubkey) AS relay_count,
+ (SELECT count(*) FROM caching_backfill_relay_progress WHERE author_pubkey = fp.pubkey AND complete = false) AS relay_incomplete
+ FROM caching_followed_pubkeys fp
+ LEFT JOIN profiles p ON p.pubkey = fp.pubkey
+ ORDER BY fp.is_root DESC, fp.events_fetched DESC LIMIT 100
+ ")->fetchAll();
+ // Fetch per-relay progress for all followed pubkeys in one query
+ $relayProgress = [];
+ try {
+ $rpRows = $pdo->query("
+ SELECT author_pubkey, relay_url, complete, events_fetched, until_cursor, last_status, updated_at
+ FROM caching_backfill_relay_progress
+ ORDER BY author_pubkey, events_fetched DESC
+ ")->fetchAll();
+ foreach ($rpRows as $rp) {
+ $relayProgress[$rp['author_pubkey']][] = $rp;
+ }
+ } catch (PDOException $e) {}
+ foreach ($follows as &$f) {
+ $f['name'] = profile_display_name($f);
+ $f['npub'] = function_exists('hex_to_npub') ? hex_to_npub($f['pubkey']) : substr($f['pubkey'], 0, 20);
+ $f['relays'] = $relayProgress[$f['pubkey']] ?? [];
+ }
+} catch (PDOException $e) {}
+
+json_response(['state' => $state, 'active' => $active, 'inbox' => $inbox, 'follows' => $follows, 'config' => $config]);
diff --git a/admin/api/chart.php b/admin/api/chart.php
new file mode 100644
index 0000000..ff16590
--- /dev/null
+++ b/admin/api/chart.php
@@ -0,0 +1,120 @@
+) and the terminal:
+ *
+ * curl http://localhost:8088/api/chart.php?range=hour
+ * curl http://localhost:8088/api/chart.php?range=day
+ * curl http://localhost:8088/api/chart.php?range=month
+ * curl http://localhost:8088/api/chart.php?range=year
+ *
+ * Caching: the hour chart is never cached (live). Day/month/year are
+ * cached to file with TTLs to avoid expensive queries on every request.
+ */
+
+require_once __DIR__ . '/../lib/helpers.php';
+require_once __DIR__ . '/../lib/ascii_chart.php';
+
+// --- Range configuration ---
+$ranges = [
+ 'hour' => ['span' => 3600, 'bin' => 45, 'bins' => 80, 'ttl' => 0, 'title' => 'New Events — Last Hour'],
+ 'day' => ['span' => 86400, 'bin' => 1080, 'bins' => 80, 'ttl' => 3600, 'title' => 'New Events — Last 24 Hours'],
+ 'month' => ['span' => 2592000, 'bin' => 32400, 'bins' => 80, 'ttl' => 86400, 'title' => 'New Events — Last 30 Days'],
+ 'year' => ['span' => 31536000, 'bin' => 394200, 'bins' => 80, 'ttl' => 2592000, 'title' => 'New Events — Last Year'],
+];
+
+$range = $_GET['range'] ?? 'hour';
+if (!isset($ranges[$range])) {
+ http_response_code(400);
+ header('Content-Type: text/plain; charset=utf-8');
+ echo "Invalid range. Use: hour, day, month, or year.\n";
+ exit;
+}
+
+$cfg = $ranges[$range];
+
+// --- Set content type ---
+header('Content-Type: text/plain; charset=utf-8');
+
+// --- Check cache ---
+if ($cfg['ttl'] > 0) {
+ $cached = get_cached_chart($range, $cfg['ttl']);
+ if ($cached !== null) {
+ echo $cached;
+ exit;
+ }
+}
+
+// --- Run binning query ---
+$pdo = db();
+$now = time();
+$epoch = $now - $cfg['span'];
+$bin_size = $cfg['bin'];
+$num_bins = $cfg['bins'];
+
+try {
+ // Calculate the bin offset so the rightmost bin aligns with "now"
+ // bin index 0 = oldest, bin index (num_bins-1) = newest
+ $base_bin = (int)floor($epoch / $bin_size);
+
+ $sql = "SELECT FLOOR(created_at / {$bin_size})::BIGINT - {$base_bin} AS bin, COUNT(*) AS cnt
+ FROM events
+ WHERE created_at >= {$epoch}
+ GROUP BY bin
+ ORDER BY bin";
+ $rows = $pdo->query($sql)->fetchAll();
+} catch (PDOException $e) {
+ echo "Chart query failed: " . $e->getMessage() . "\n";
+ exit;
+}
+
+// --- Build bin array (zero-filled, fixed length) ---
+// Index 0 = oldest, index N-1 = newest (natural time order from SQL)
+$bins = build_bin_array($rows, $num_bins);
+
+// Reverse so newest bin is at index 0 (leftmost) and oldest at right.
+// This matches the original text_graph.js display: new data enters
+// from the left and proceeds rightward as it ages.
+$bins = array_reverse($bins);
+
+// --- Render ASCII chart ---
+$ascii = render_ascii_chart($bins, [
+ 'title' => $cfg['title'],
+ 'max_height' => 11,
+ 'bin_duration' => $bin_size,
+ 'label_interval' => max(1, (int)floor($num_bins / 15)), // ~15 labels across
+]);
+
+// --- Write to cache (if TTL > 0) ---
+if ($cfg['ttl'] > 0) {
+ set_cached_chart($range, $ascii);
+}
+
+echo $ascii;
+
+
+// ================================
+// CACHE HELPERS
+// ================================
+
+function get_cache_dir(): string {
+ return __DIR__ . '/../cache';
+}
+
+function get_cached_chart(string $range, int $ttl): ?string {
+ $file = get_cache_dir() . "/chart_{$range}.txt";
+ if (!file_exists($file)) return null;
+ if (filemtime($file) < (time() - $ttl)) return null;
+ $content = @file_get_contents($file);
+ return ($content !== false) ? $content : null;
+}
+
+function set_cached_chart(string $range, string $ascii): void {
+ $dir = get_cache_dir();
+ if (!is_dir($dir)) {
+ @mkdir($dir, 0775, true);
+ }
+ @file_put_contents($dir . "/chart_{$range}.txt", $ascii);
+}
diff --git a/admin/api/config.php b/admin/api/config.php
new file mode 100644
index 0000000..423b3d8
--- /dev/null
+++ b/admin/api/config.php
@@ -0,0 +1,20 @@
+ 'Missing key']);
+ try {
+ $pdo->prepare("UPDATE config SET value = ? WHERE key = ?")->execute([$value, $key]);
+ json_response(['message' => "Updated $key"]);
+ } catch (PDOException $e) {
+ json_response(['error' => $e->getMessage()]);
+ }
+}
+
+$rows = $pdo->query("SELECT key, value FROM config ORDER BY key")->fetchAll();
+json_response(['config' => $rows]);
diff --git a/admin/api/dm.php b/admin/api/dm.php
new file mode 100644
index 0000000..9b27d08
--- /dev/null
+++ b/admin/api/dm.php
@@ -0,0 +1,16 @@
+prepare("SELECT id, pubkey, kind, created_at, content FROM events WHERE kind IN (4, 14, 15) ORDER BY created_at DESC LIMIT $limit");
+$rows->execute();
+$messages = $rows->fetchAll();
+
+foreach ($messages as &$m) {
+ $m['created_at'] = date('Y-m-d H:i:s', intval($m['created_at']));
+ $m['content'] = substr($m['content'] ?? '', 0, 300);
+}
+
+json_response(['messages' => $messages]);
diff --git a/admin/api/events.php b/admin/api/events.php
new file mode 100644
index 0000000..d7f7dde
--- /dev/null
+++ b/admin/api/events.php
@@ -0,0 +1,32 @@
+prepare("SELECT e.id, e.pubkey, e.kind, e.created_at, e.content,
+ p.name, p.display_name
+ FROM events e
+ LEFT JOIN profiles p ON p.pubkey = e.pubkey
+ $where_sql
+ ORDER BY e.created_at DESC LIMIT $limit OFFSET $offset");
+$rows->execute($params);
+$events = $rows->fetchAll();
+
+foreach ($events as &$e) {
+ $e['created_at'] = date('Y-m-d H:i:s', intval($e['created_at']));
+ $e['content'] = substr($e['content'] ?? '', 0, 200);
+ // Resolve display name from profiles cache; fall back to truncated pubkey.
+ $best = profile_display_name($e);
+ $e['display_name'] = $best !== '' ? $best : substr($e['pubkey'] ?? '', 0, 16) . '…';
+}
+
+json_response(['events' => $events]);
diff --git a/admin/api/ipbans.php b/admin/api/ipbans.php
new file mode 100644
index 0000000..5e670ae
--- /dev/null
+++ b/admin/api/ipbans.php
@@ -0,0 +1,35 @@
+prepare("INSERT INTO ip_bans (ip, banned_until, ban_count) VALUES (?, ?, 1) ON CONFLICT (ip) DO UPDATE SET banned_until = ?, ban_count = ip_bans.ban_count + 1")->execute([$input['ip'], $until, $until]);
+ json_response(['message' => 'IP banned']);
+ } elseif ($action === 'remove') {
+ $pdo->prepare("DELETE FROM ip_bans WHERE ip = ?")->execute([$input['ip']]);
+ json_response(['message' => 'Ban removed']);
+ }
+ } catch (PDOException $e) { json_response(['error' => $e->getMessage()]); }
+}
+
+$now = time();
+$total = 0; $active = 0; $issued = 0;
+$bans = [];
+try {
+ $total = intval($pdo->query("SELECT count(*) FROM ip_bans")->fetchColumn());
+ $active = intval($pdo->query("SELECT count(*) FROM ip_bans WHERE banned_until > $now")->fetchColumn());
+ $issued = intval($pdo->query("SELECT COALESCE(sum(ban_count),0) FROM ip_bans")->fetchColumn());
+ $bans = $pdo->query("SELECT ip, banned_until, ban_count, failures, authed_successfully, connection_attempts FROM ip_bans ORDER BY banned_until DESC LIMIT 100")->fetchAll();
+ foreach ($bans as &$b) {
+ $b['status'] = intval($b['banned_until']) > $now ? 'banned' : 'expired';
+ $b['banned_until'] = date('Y-m-d H:i:s', intval($b['banned_until']));
+ }
+} catch (PDOException $e) {}
+
+json_response(['total' => $total, 'active' => $active, 'issued' => $issued, 'bans' => $bans]);
diff --git a/admin/api/profile.php b/admin/api/profile.php
new file mode 100644
index 0000000..e28a9f9
--- /dev/null
+++ b/admin/api/profile.php
@@ -0,0 +1,38 @@
+
+ * Returns: { "pubkey": "...", "name": "...", "display_name": "...",
+ * "best_name": "...", "picture": "...", "nip05": "...",
+ * "about": "...", "website": "...", "lud16": "..." }
+ * or { "error": "not found" } with HTTP 404 if no profile is cached.
+ */
+require_once __DIR__ . '/../lib/helpers.php';
+
+$pubkey = trim($_GET['pubkey'] ?? '');
+if (!preg_match('/^[0-9a-fA-F]{64}$/', $pubkey)) {
+ http_response_code(400);
+ json_response(['error' => 'Invalid pubkey']);
+}
+
+$pdo = db();
+try {
+ $stmt = $pdo->prepare(
+ "SELECT pubkey, name, display_name, about, picture, banner,
+ nip05, website, lud16, lud06
+ FROM profiles WHERE pubkey = ?"
+ );
+ $stmt->execute([$pubkey]);
+ $row = $stmt->fetch();
+ if (!$row) {
+ http_response_code(404);
+ json_response(['error' => 'not found']);
+ }
+ $row['best_name'] = profile_display_name($row);
+ json_response($row);
+} catch (PDOException $e) {
+ http_response_code(500);
+ json_response(['error' => 'Database error']);
+}
diff --git a/admin/api/query.php b/admin/api/query.php
new file mode 100644
index 0000000..6228713
--- /dev/null
+++ b/admin/api/query.php
@@ -0,0 +1,24 @@
+ 'POST only']);
+}
+
+$input = json_decode(file_get_contents('php://input'), true);
+$sql = trim($input['sql'] ?? '');
+
+if (!$sql) json_response(['error' => 'No query provided']);
+if (!preg_match('/^\s*SELECT/i', $sql)) json_response(['error' => 'Only SELECT queries are allowed']);
+
+$start = microtime(true);
+try {
+ $stmt = $pdo->query($sql);
+ $rows = $stmt->fetchAll();
+ $time_ms = round((microtime(true) - $start) * 1000, 1);
+ json_response(['rows' => $rows, 'row_count' => count($rows), 'time_ms' => $time_ms]);
+} catch (PDOException $e) {
+ json_response(['error' => $e->getMessage()]);
+}
diff --git a/admin/api/stats.php b/admin/api/stats.php
new file mode 100644
index 0000000..cad2b9a
--- /dev/null
+++ b/admin/api/stats.php
@@ -0,0 +1,154 @@
+query("SELECT pg_database_size(current_database())")->fetchColumn();
+ $db_size = format_bytes(intval($bytes));
+} catch (PDOException $e) {}
+
+// Total events
+$total_events = intval($pdo->query("SELECT COUNT(*) FROM events")->fetchColumn());
+
+// Event rate: events with first_seen in last 10 seconds
+$events_delta = 0;
+try {
+ $events_delta = intval($pdo->query("SELECT COUNT(*) FROM events WHERE first_seen >= EXTRACT(EPOCH FROM NOW())::BIGINT - 10")->fetchColumn());
+} catch (PDOException $e) {}
+
+// Process info (from pg_stat_activity)
+$process_id = '-';
+$ws_connections = '-';
+try {
+ $pid = $pdo->query("SELECT pg_backend_pid()")->fetchColumn();
+ $process_id = strval($pid);
+ $ws_connections = intval($pdo->query("SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND pid != pg_backend_pid()")->fetchColumn());
+} catch (PDOException $e) {}
+
+// Active subscriptions
+$active_subscriptions = 0;
+try {
+ $active_subscriptions = intval($pdo->query("SELECT count(*) FROM subscriptions WHERE active = true")->fetchColumn());
+} catch (PDOException $e) {}
+
+// Memory/CPU (from /proc on Linux)
+$memory_usage = '-';
+$cpu_usage = '-';
+$cpu_core = '-';
+if (is_readable('/proc/meminfo')) {
+ $mem = parse_ini_file('/proc/meminfo');
+ $total = intval($mem['MemTotal'] ?? 0) * 1024;
+ $avail = intval($mem['MemAvailable'] ?? 0) * 1024;
+ if ($total > 0) $memory_usage = format_bytes($total - $avail) . ' / ' . format_bytes($total);
+}
+if (is_readable('/proc/cpuinfo')) {
+ $cores = intval(shell_exec('nproc 2>/dev/null') ?: 1);
+ $cpu_core = strval($cores) . ' cores';
+}
+$cpu_usage = @file_get_contents('/proc/loadavg');
+if ($cpu_usage !== false) $cpu_usage = trim(explode(' ', $cpu_usage)[0] ?? '-');
+
+// Oldest / newest event
+$oldest_event = '-';
+$newest_event = '-';
+try {
+ $oldest = $pdo->query("SELECT to_timestamp(MIN(created_at)) FROM events")->fetchColumn();
+ $newest = $pdo->query("SELECT to_timestamp(MAX(created_at)) FROM events")->fetchColumn();
+ if ($oldest) $oldest_event = substr($oldest, 0, 19);
+ if ($newest) $newest_event = substr($newest, 0, 19);
+} catch (PDOException $e) {}
+
+// Time-based stats
+$now = time();
+$events_24h = 0; $events_7d = 0; $events_30d = 0;
+try {
+ $events_24h = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 86400")->fetchColumn());
+ $events_7d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 604800")->fetchColumn());
+ $events_30d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 2592000")->fetchColumn());
+} catch (PDOException $e) {}
+
+// Kind distribution
+$kinds = [];
+try {
+ $rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 20")->fetchAll();
+ foreach ($rows as $r) {
+ $kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
+ }
+} catch (PDOException $e) {}
+
+// Top pubkeys (with names from profiles cache)
+$top_pubkeys = [];
+try {
+ $rows = $pdo->query("
+ SELECT pubkey, COUNT(*) AS cnt
+ FROM events
+ GROUP BY pubkey
+ ORDER BY cnt DESC LIMIT 20
+ ")->fetchAll();
+ // Batch-resolve profile names from the profiles cache table.
+ $pubkeys = array_column($rows, 'pubkey');
+ $pmap = profile_map($pubkeys);
+ foreach ($rows as $r) {
+ $pk = $r['pubkey'];
+ $prof = $pmap[$pk] ?? null;
+ $top_pubkeys[] = [
+ 'pubkey' => $pk,
+ 'name' => $prof ? $prof['best_name'] : '',
+ 'count' => intval($r['cnt']),
+ 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
+ ];
+ }
+} catch (PDOException $e) {}
+
+// Name-field usage stats (from profiles cache)
+$name_field_usage = ['both' => 0, 'name_only' => 0, 'display_only' => 0, 'neither' => 0, 'both_differ' => 0, 'total' => 0];
+try {
+ $row = $pdo->query("
+ SELECT count(*) FILTER (WHERE name <> '' AND display_name <> '') AS both,
+ count(*) FILTER (WHERE name <> '' AND display_name = '') AS name_only,
+ count(*) FILTER (WHERE name = '' AND display_name <> '') AS display_only,
+ count(*) FILTER (WHERE name = '' AND display_name = '') AS neither,
+ count(*) FILTER (WHERE name <> '' AND display_name <> '' AND name <> display_name) AS both_differ,
+ count(*) AS total
+ FROM profiles
+ ")->fetch();
+ if ($row) {
+ $name_field_usage = array_map('intval', $row);
+ }
+} catch (PDOException $e) {}
+
+json_response([
+ 'db_size' => $db_size,
+ 'total_events' => $total_events,
+ 'events_delta' => $events_delta,
+ 'process_id' => $process_id,
+ 'ws_connections' => $ws_connections,
+ 'active_subscriptions' => $active_subscriptions,
+ 'memory_usage' => $memory_usage,
+ 'cpu_usage' => $cpu_usage,
+ 'cpu_core' => $cpu_core,
+ 'oldest_event' => $oldest_event,
+ 'newest_event' => $newest_event,
+ 'events_24h' => $events_24h,
+ 'events_7d' => $events_7d,
+ 'events_30d' => $events_30d,
+ 'kinds' => $kinds,
+ 'top_pubkeys' => $top_pubkeys,
+ 'name_field_usage' => $name_field_usage,
+]);
+
+/** Format bytes as human-readable. */
+function format_bytes(int $bytes): string {
+ if ($bytes < 1024) return $bytes . ' B';
+ if ($bytes < 1048576) return round($bytes / 1024, 1) . ' KB';
+ if ($bytes < 1073741824) return round($bytes / 1048576, 1) . ' MB';
+ return round($bytes / 1073741824, 2) . ' GB';
+}
diff --git a/admin/api/status.php b/admin/api/status.php
deleted file mode 100644
index 897df67..0000000
--- a/admin/api/status.php
+++ /dev/null
@@ -1,69 +0,0 @@
-query("SELECT * FROM caching_service_state WHERE id = 1")->fetch();
-
-// Active backfill target (table may not exist yet on fresh start)
-$active = ['active_pubkey' => '', 'active_relay' => ''];
-try {
- $active = $pdo->query("SELECT active_pubkey, active_relay FROM caching_backfill_active WHERE id = 1")->fetch() ?: $active;
-} catch (PDOException $e) { /* table doesn't exist yet */ }
-
-// Error relay summary (consecutive_errors column may not exist yet on fresh start)
-$error_stats = ['auto_completed' => 0, 'error_count' => 0, 'timeout_count' => 0];
-try {
- $error_stats = $pdo->query("
- SELECT
- COUNT(*) FILTER (WHERE consecutive_errors >= 3) AS auto_completed,
- COUNT(*) FILTER (WHERE last_status LIKE 'error%') AS error_count,
- COUNT(*) FILTER (WHERE last_status = 'timeout') AS timeout_count
- FROM caching_backfill_relay_progress
- ")->fetch() ?: $error_stats;
-} catch (PDOException $e) { /* column/table doesn't exist yet */ }
-
-// Inbox stats
-$inbox = $pdo->query("
- SELECT
- COUNT(*) AS pending,
- COUNT(*) FILTER (WHERE source_class = 'live') AS live,
- COUNT(*) FILTER (WHERE source_class = 'backfill') AS backfill
- FROM caching_event_inbox
-")->fetch();
-
-// Event count
-$events = $pdo->query("SELECT COUNT(*) AS total FROM events")->fetch();
-
-// Heartbeat age
-$hb_age = intval($state['heartbeat_at'] ?? 0);
-$hb_ago = $hb_age > 0 ? time() - $hb_age : 0;
-
-json_response([
- 'service_state' => $state['service_state'] ?? 'unknown',
- 'heartbeat_ago' => $hb_ago > 0 ? $hb_ago . 's ago' : 'never',
- 'config_generation' => intval($state['config_generation'] ?? 0),
- 'followed_count' => intval($state['followed_author_count'] ?? 0),
- 'selected_relays' => intval($state['selected_relay_count'] ?? 0),
- 'connected_relays' => intval($state['connected_relay_count'] ?? 0),
- 'bf_complete' => intval($state['backfill_authors_complete'] ?? 0),
- 'bf_total' => intval($state['backfill_authors_total'] ?? 0),
- 'events_fetched' => intval($state['events_fetched'] ?? 0),
- 'inbox_inserts' => intval($state['inbox_inserts'] ?? 0),
- 'db_events' => intval($events['total'] ?? 0),
- 'inbox_pending' => intval($inbox['pending'] ?? 0),
- 'inbox_live' => intval($inbox['live'] ?? 0),
- 'inbox_backfill' => intval($inbox['backfill'] ?? 0),
- 'error_count' => intval($error_stats['error_count'] ?? 0),
- 'timeout_count' => intval($error_stats['timeout_count'] ?? 0),
- 'auto_completed' => intval($error_stats['auto_completed'] ?? 0),
- 'active_pubkey' => $active['active_pubkey'] ?? '',
- 'active_relay' => $active['active_relay'] ?? '',
-]);
diff --git a/admin/api/subscriptions.php b/admin/api/subscriptions.php
new file mode 100644
index 0000000..471875b
--- /dev/null
+++ b/admin/api/subscriptions.php
@@ -0,0 +1,11 @@
+query("SELECT id, sub_id, pubkey, filters, created_at, events_sent FROM subscriptions ORDER BY created_at DESC LIMIT 100")->fetchAll();
+} catch (PDOException $e) {}
+
+json_response(['subscriptions' => $subs]);
diff --git a/admin/assets/app.js b/admin/assets/app.js
new file mode 100644
index 0000000..6fac216
--- /dev/null
+++ b/admin/assets/app.js
@@ -0,0 +1,952 @@
+/*
+ * admin2 — Full PHP admin for C-Relay-PG.
+ * Replaces the 7457-line api/index.js with a lightweight AJAX-based
+ * controller that fetches data from PHP API endpoints (api/*.php)
+ * instead of Nostr WebSocket admin commands.
+ */
+
+const REFRESH_MS = 10000;
+let currentPage = 'statistics';
+let statsInterval = null;
+
+// HTML-escape helper for safe interpolation into innerHTML.
+// Prevents stored XSS from user-controlled data (profile names, event
+// content, config values, etc.) being parsed as HTML by the browser.
+const esc = (s) => {
+ const str = String(s ?? '');
+ return str.replace(/[&<>"']/g, (ch) => {
+ return '' + ch.charCodeAt(0) + ';';
+ });
+};
+
+// Auth state
+let nlLite = null;
+let userPubkey = null;
+let isLoggedIn = false;
+let relayAnimationTimer = null;
+
+// Server-rendered ASCII chart state
+let currentChartRange = 'hour';
+let chartLoadTimer = null;
+
+// ================================
+// NAVIGATION
+// ================================
+
+function toggleNav() {
+ document.getElementById('side-nav').classList.toggle('open');
+ document.getElementById('side-nav-overlay').classList.toggle('show');
+}
+
+document.getElementById('side-nav-overlay')?.addEventListener('click', toggleNav);
+
+document.querySelectorAll('.nav-item').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const page = btn.getAttribute('data-page');
+ switchPage(page);
+ document.getElementById('side-nav').classList.remove('open');
+ document.getElementById('side-nav-overlay').classList.remove('show');
+ });
+});
+
+function switchPage(pageName) {
+ currentPage = pageName;
+ document.querySelectorAll('.nav-item').forEach(item => {
+ item.classList.remove('active');
+ if (item.getAttribute('data-page') === pageName) item.classList.add('active');
+ });
+
+ const sections = [
+ 'databaseStatisticsSection', 'subscriptionDetailsSection', 'div_config',
+ 'authRulesSection', 'wotSection', 'ipBansSection', 'relayEventsSection',
+ 'cachingSection', 'nip17DMSection', 'sqlQuerySection'
+ ];
+ sections.forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; });
+
+ const pageMap = {
+ 'statistics': 'databaseStatisticsSection',
+ 'subscriptions': 'subscriptionDetailsSection',
+ 'configuration': 'div_config',
+ 'ip-bans': 'ipBansSection',
+ 'relay-events': 'relayEventsSection',
+ 'caching': 'cachingSection',
+ 'dm': 'nip17DMSection',
+ 'database': 'sqlQuerySection'
+ };
+
+ if (pageName === 'authorization') {
+ document.getElementById('authRulesSection').style.display = 'block';
+ document.getElementById('wotSection').style.display = 'block';
+ loadAuthRules();
+ loadWotStatus();
+ } else {
+ const target = pageMap[pageName];
+ if (target) document.getElementById(target).style.display = 'block';
+ }
+
+ // Load data for the page
+ const loaders = {
+ 'statistics': loadStats,
+ 'subscriptions': loadSubscriptions,
+ 'configuration': loadConfig,
+ 'ip-bans': loadIpBans,
+ 'relay-events': loadEvents,
+ 'caching': loadCaching,
+ 'dm': loadDMs,
+ };
+ if (loaders[pageName]) loaders[pageName]();
+
+ // Start/stop auto-refresh for statistics
+ if (pageName === 'statistics') {
+ if (!statsInterval) {
+ loadStats();
+ statsInterval = setInterval(loadStats, REFRESH_MS);
+ console.log('[admin2] auto-refresh started, interval:', REFRESH_MS, 'ms');
+ }
+ } else {
+ if (statsInterval) { clearInterval(statsInterval); statsInterval = null; }
+ }
+}
+
+// ================================
+// STATISTICS
+// ================================
+
+async function loadStats() {
+ console.log('[admin2] loading stats at', new Date().toLocaleTimeString());
+ // Fire the RELAY letter animation on every refresh — visual indicator the page is updating
+ startRelayAnimation();
+ try {
+ const res = await fetch('api/stats.php');
+ if (!res.ok) { console.warn('[admin2] stats.php returned', res.status); return; }
+ const d = await res.json();
+ console.log('[admin2] stats loaded — events:', d.total_events, 'rate:', d.events_delta, '/10s');
+
+ const set = (id, val) => { const el = document.getElementById(id); if (el) el.textContent = val; };
+ set('db-size', d.db_size || '-');
+ set('total-events', (d.total_events ?? 0).toLocaleString());
+ set('process-id', d.process_id || '-');
+ set('websocket-connections', d.ws_connections ?? '-');
+ set('active-subscriptions', d.active_subscriptions ?? '-');
+ set('memory-usage', d.memory_usage || '-');
+ set('cpu-usage', d.cpu_usage || '-');
+ set('cpu-core', d.cpu_core || '-');
+ set('oldest-event', d.oldest_event || '-');
+ set('newest-event', d.newest_event || '-');
+ set('events-24h', (d.events_24h ?? 0).toLocaleString());
+ set('events-7d', (d.events_7d ?? 0).toLocaleString());
+ set('events-30d', (d.events_30d ?? 0).toLocaleString());
+
+ // Kind distribution
+ if (d.kinds) {
+ const tbody = document.getElementById('stats-kinds-table-body');
+ tbody.innerHTML = d.kinds.map(k =>
+ `
${k.kind} ${k.count.toLocaleString()} ${k.pct}% `
+ ).join('');
+ }
+
+ // Top pubkeys
+ if (d.top_pubkeys) {
+ const tbody = document.getElementById('stats-pubkeys-table-body');
+ tbody.innerHTML = d.top_pubkeys.map((p, i) =>
+ `${i+1} ${esc(p.name) || 'unknown '} ${esc(p.pubkey.substring(0,16))}… ${p.count.toLocaleString()} ${p.pct}% `
+ ).join('');
+ }
+
+ // Name-field usage stats (from profiles cache)
+ if (d.name_field_usage && d.name_field_usage.total > 0) {
+ const u = d.name_field_usage;
+ const el = document.getElementById('name-field-usage');
+ if (el) {
+ el.innerHTML = `
+
+ Profile name fields (${u.total} profiles):
+ Both: ${u.both}
+ name only: ${u.name_only}
+ display_name only: ${u.display_only}
+ Differ: ${u.both_differ}
+
`;
+ }
+ }
+
+ // Refresh the chart on every stats poll (server handles caching for non-hour ranges)
+ loadChart(currentChartRange);
+ } catch (e) { console.error('[admin2] stats error:', e); }
+}
+
+// ================================
+// EVENT RATE CHART (server-rendered ASCII via chart.php)
+// ================================
+
+// Fetch the ASCII chart from the server and inject it into the div.
+// The chart is rendered server-side as plain text — works in both
+// browser and terminal (curl http://localhost:8088/api/chart.php?range=hour)
+async function loadChart(range) {
+ const el = document.getElementById('event-rate-chart');
+ if (!el) return;
+ try {
+ const res = await fetch('api/chart.php?range=' + encodeURIComponent(range));
+ if (!res.ok) { el.textContent = 'Chart load failed (HTTP ' + res.status + ')'; return; }
+ const text = await res.text();
+ el.textContent = text;
+ // Newest data is at the left (index 0) — scroll to start so it's visible
+ el.scrollLeft = 0;
+ } catch (e) {
+ console.error('[admin2] chart load error:', e);
+ el.textContent = 'Chart load error: ' + e.message;
+ }
+}
+
+// Chart range tab click handlers
+document.querySelectorAll('.chart-tab').forEach(tab => {
+ tab.addEventListener('click', () => {
+ const range = tab.getAttribute('data-range');
+ if (!range || range === currentChartRange) return;
+ currentChartRange = range;
+ document.querySelectorAll('.chart-tab').forEach(t => t.classList.remove('active'));
+ tab.classList.add('active');
+ loadChart(range);
+ });
+});
+
+// ================================
+// SUBSCRIPTIONS
+// ================================
+
+async function loadSubscriptions() {
+ try {
+ const res = await fetch('api/subscriptions.php');
+ const d = await res.json();
+ const tbody = document.getElementById('subscription-details-table-body');
+ if (!d.subscriptions || d.subscriptions.length === 0) {
+ tbody.innerHTML = 'No subscriptions active ';
+ return;
+ }
+ tbody.innerHTML = d.subscriptions.map(s =>
+ `${esc(s.sub_id) || '-'} ${esc((s.pubkey||'-').substring(0,16))} ${esc(s.filters) || '-'} ${esc(s.created) || '-'} ${s.events_sent ?? 0} `
+ ).join('');
+ } catch (e) { console.error('[admin2] subscriptions error:', e); }
+}
+
+// ================================
+// CONFIGURATION
+// ================================
+
+async function loadConfig() {
+ try {
+ const res = await fetch('api/config.php');
+ const d = await res.json();
+ const tbody = document.getElementById('config-table-body');
+ if (!d.config || d.config.length === 0) {
+ tbody.innerHTML = 'No config entries ';
+ return;
+ }
+ tbody.innerHTML = d.config.map(c =>
+ `${esc(c.key)} ${esc(c.value)} EDIT `
+ ).join('');
+ } catch (e) { console.error('[admin2] config error:', e); }
+}
+
+function editConfig(key) {
+ const newVal = prompt('Enter new value for ' + key + ':');
+ if (newVal === null) return;
+ fetch('api/config.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({key, value: newVal})
+ }).then(r => r.json()).then(d => {
+ alert(d.message || 'Saved');
+ loadConfig();
+ }).catch(e => alert('Error: ' + e));
+}
+
+// ================================
+// AUTH RULES
+// ================================
+
+async function loadAuthRules() {
+ try {
+ const res = await fetch('api/auth.php');
+ const d = await res.json();
+ const tbody = document.getElementById('authRulesTableBody');
+ if (!d.rules || d.rules.length === 0) {
+ tbody.innerHTML = 'No auth rules ';
+ return;
+ }
+ tbody.innerHTML = d.rules.map(r =>
+ `${esc(r.rule_type)} ${esc(r.pattern_type)} ${esc(r.pattern_value)} ${esc(r.status) || 'active'} REMOVE `
+ ).join('');
+ } catch (e) { console.error('[admin2] auth error:', e); }
+}
+
+async function addAuthRule(type) {
+ const pk = document.getElementById('authRulePubkey').value.trim();
+ if (!pk) { alert('Enter a pubkey first'); return; }
+ const res = await fetch('api/auth.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'add', rule_type: type, pattern_value: pk})
+ });
+ const d = await res.json();
+ alert(d.message || 'Done');
+ loadAuthRules();
+}
+
+async function removeAuthRule(id) {
+ const res = await fetch('api/auth.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'remove', id})
+ });
+ const d = await res.json();
+ alert(d.message || 'Done');
+ loadAuthRules();
+}
+
+// ================================
+// WEB OF TRUST
+// ================================
+
+async function loadWotStatus() {
+ try {
+ const res = await fetch('api/auth.php?action=wot');
+ const d = await res.json();
+ const ind = document.getElementById('wotKind3Indicator');
+ if (ind) ind.textContent = d.kind3_status || 'Unknown';
+ const cnt = document.getElementById('wotWhitelistCount');
+ if (cnt) cnt.textContent = d.whitelist_count ?? '—';
+ const desc = document.getElementById('wotLevelDescription');
+ if (desc) desc.textContent = d.level_description || '';
+ } catch (e) { console.error('[admin2] wot error:', e); }
+}
+
+async function setWotLevel(level) {
+ const res = await fetch('api/auth.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'set_wot_level', level})
+ });
+ const d = await res.json();
+ alert(d.message || 'Done');
+ loadWotStatus();
+}
+
+async function syncWot() {
+ const res = await fetch('api/auth.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'sync_wot'})
+ });
+ const d = await res.json();
+ alert(d.message || 'Done');
+ loadWotStatus();
+}
+
+// ================================
+// IP BANS
+// ================================
+
+async function loadIpBans() {
+ try {
+ const res = await fetch('api/ipbans.php');
+ const d = await res.json();
+ document.getElementById('ip-bans-total').textContent = d.total ?? '-';
+ document.getElementById('ip-bans-active').textContent = d.active ?? '-';
+ document.getElementById('ip-bans-issued').textContent = d.issued ?? '-';
+ const tbody = document.getElementById('ip-bans-tbody');
+ if (!d.bans || d.bans.length === 0) {
+ tbody.innerHTML = 'No IP bans ';
+ return;
+ }
+ tbody.innerHTML = d.bans.map(b =>
+ `${esc(b.ip)} ${esc(b.status)} ${esc(b.banned_until)} ${b.failures ?? 0} REMOVE `
+ ).join('');
+ } catch (e) { console.error('[admin2] ipbans error:', e); }
+}
+
+async function addBan() {
+ const ip = document.getElementById('ban-ip-input').value.trim();
+ const duration = document.getElementById('ban-duration-select').value;
+ if (!ip) { alert('Enter an IP address'); return; }
+ const res = await fetch('api/ipbans.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'add', ip, duration: parseInt(duration)})
+ });
+ const d = await res.json();
+ document.getElementById('add-ban-status').textContent = d.message || 'Done';
+ loadIpBans();
+}
+
+async function removeBan(ip) {
+ const res = await fetch('api/ipbans.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'remove', ip})
+ });
+ const d = await res.json();
+ alert(d.message || 'Done');
+ loadIpBans();
+}
+
+// ================================
+// RELAY EVENTS
+// ================================
+
+async function loadEvents() {
+ try {
+ const res = await fetch('api/events.php?limit=50');
+ const d = await res.json();
+ const tbody = document.getElementById('live-relay-events-table-body');
+ if (!d.events || d.events.length === 0) {
+ tbody.innerHTML = 'No events ';
+ return;
+ }
+ tbody.innerHTML = d.events.map(e =>
+ `${esc(e.created_at)} ${e.kind} ${esc(e.display_name || (e.pubkey||'').substring(0,16) + '…')} ${esc((e.id||'').substring(0,16))}… ${esc((e.content||'').substring(0,80))} `
+ ).join('');
+ } catch (err) { console.error('[admin2] events error:', err); }
+}
+
+// ================================
+// CACHING
+// ================================
+
+async function loadCaching() {
+ try {
+ const res = await fetch('api/caching.php');
+ const d = await res.json();
+ // Config toggle state
+ if (d.config) {
+ const enabled = d.config.caching_enabled === 'true';
+ const inboxEnabled = d.config.caching_inbox_enabled === 'true';
+ const enLabel = document.getElementById('caching-enabled-label');
+ const enBtn = document.getElementById('caching-toggle-btn');
+ const inLabel = document.getElementById('caching-inbox-enabled-label');
+ const inBtn = document.getElementById('caching-inbox-toggle-btn');
+ if (enLabel) enLabel.textContent = 'Caching: ' + (enabled ? 'ON' : 'OFF');
+ if (enBtn) enBtn.textContent = enabled ? 'Turn OFF' : 'Turn ON';
+ if (inLabel) inLabel.textContent = 'Inbox: ' + (inboxEnabled ? 'ON' : 'OFF');
+ if (inBtn) inBtn.textContent = inboxEnabled ? 'Turn OFF' : 'Turn ON';
+ }
+ // Service status
+ const ssEl = document.getElementById('caching-service-status');
+ if (ssEl && d.state) {
+ const s = d.state;
+ const hb = s.heartbeat_at ? new Date(s.heartbeat_at * 1000).toLocaleTimeString() : '—';
+ ssEl.innerHTML = `State: ${esc(s.service_state)} | Follows: ${s.followed_author_count ?? 0} | Connected relays: ${s.connected_relay_count ?? 0}/${s.selected_relay_count ?? 0} | Backfill: ${s.backfill_authors_complete ?? 0}/${s.backfill_authors_total ?? 0} | Events fetched: ${s.events_fetched ?? 0} | Inbox inserts: ${s.inbox_inserts ?? 0} | Heartbeat: ${hb}
`;
+ }
+ // Inbox status
+ const isEl = document.getElementById('caching-inbox-status');
+ if (isEl && d.inbox) {
+ isEl.innerHTML = `Pending: ${d.inbox.pending} | Live: ${d.inbox.live} | Backfill: ${d.inbox.backfill}
`;
+ }
+ // Follows table
+ const tbody = document.getElementById('caching-follows-table-body');
+ if (tbody && d.follows) {
+ if (d.follows.length === 0) {
+ tbody.innerHTML = 'No followed pubkeys ';
+ } else {
+ tbody.innerHTML = d.follows.map((f, i) => {
+ const npub = f.npub || f.pubkey.substring(0, 20);
+ const relaySummary = f.relay_incomplete > 0
+ ? `${f.relay_count - f.relay_incomplete}/${f.relay_count} done `
+ : `${f.relay_count ?? 0} relays`;
+ // Per-relay detail row (hidden by default, toggle via click)
+ let relayDetail = '';
+ if (f.relays && f.relays.length > 0) {
+ relayDetail = '' +
+ f.relays.map(r => {
+ const statusIcon = r.complete ? '✓' : '⏳';
+ const statusBadge = r.complete
+ ? `
${esc(r.last_status || 'eose')} `
+ : `
${esc(r.last_status || 'pending')} `;
+ const relayHost = r.relay_url.replace(/^wss?:\/\//, '').replace(/\/relay$/, '');
+ return `
${statusIcon} ${esc(relayHost)} ${r.events_fetched} evts ${statusBadge}
`;
+ }).join('') +
+ '
';
+ }
+ const refreshBtn = `↻ Refresh this user `;
+ return `${esc(f.name) || 'unknown '} ${esc(npub)}… ${f.is_root ? '✓' : ''} ${f.total_events ?? 0} ${f.backfill_complete ? '✓' : '…'} ${relaySummary} ${relayDetail || '
No relay progress data '}
${refreshBtn}
`;
+ }).join('');
+ }
+ }
+ // Active target
+ const fsEl = document.getElementById('caching-follows-status');
+ if (fsEl) {
+ if (d.active && d.active.pubkey && d.active.relay) {
+ fsEl.innerHTML = `⚡ Backfilling: ${d.active.pubkey.substring(0,16)}… @ ${esc(d.active.relay)} `;
+ } else if (d.state && d.state.service_state === 'running') {
+ const incomplete = d.state.backfill_authors_complete < d.state.backfill_authors_total;
+ fsEl.innerHTML = incomplete
+ ? `⚡ Backfill in progress… `
+ : `✓ Backfill complete — listening for live events `;
+ } else {
+ fsEl.innerHTML = `✓ Caching complete `;
+ }
+ }
+ } catch (e) { console.error('[admin2] caching error:', e); }
+}
+
+// Toggle caching_enabled config via the config API.
+async function toggleCachingEnabled() {
+ try {
+ const res = await fetch('api/caching.php');
+ const d = await res.json();
+ const current = d.config?.caching_enabled === 'true';
+ const newVal = current ? 'false' : 'true';
+ await fetch('api/config.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({key: 'caching_enabled', value: newVal})
+ });
+ loadCaching();
+ } catch (e) { console.error('[admin2] toggle caching error:', e); }
+}
+
+// Toggle caching_inbox_enabled config via the config API.
+async function toggleCachingInboxEnabled() {
+ try {
+ const res = await fetch('api/caching.php');
+ const d = await res.json();
+ const current = d.config?.caching_inbox_enabled === 'true';
+ const newVal = current ? 'false' : 'true';
+ await fetch('api/config.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({key: 'caching_inbox_enabled', value: newVal})
+ });
+ loadCaching();
+ } catch (e) { console.error('[admin2] toggle inbox error:', e); }
+}
+
+// Re-run all caching: resets backfill progress for all followed authors and
+// bumps caching_config_generation so the running service hot-reloads and
+// re-drains from the beginning.
+async function rerunAllCaching() {
+ if (!confirm('Reset backfill progress for ALL followed authors? The caching service will re-download everything from scratch.')) return;
+ const btn = document.getElementById('caching-rerun-all-btn');
+ if (btn) { btn.disabled = true; btn.textContent = 'Resetting…'; }
+ try {
+ const res = await fetch('api/caching.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'reset_all'})
+ });
+ const d = await res.json();
+ if (d.ok) {
+ if (btn) { btn.textContent = '✓ Reset — re-draining'; }
+ setTimeout(() => { if (btn) { btn.disabled = false; btn.textContent = 'Re-run All Caching'; } loadCaching(); }, 2000);
+ } else {
+ alert('Reset failed: ' + (d.error || 'unknown error'));
+ if (btn) { btn.disabled = false; btn.textContent = 'Re-run All Caching'; }
+ }
+ } catch (e) {
+ console.error('[admin2] rerunAllCaching error:', e);
+ alert('Reset failed: ' + e.message);
+ if (btn) { btn.disabled = false; btn.textContent = 'Re-run All Caching'; }
+ }
+}
+
+// Refresh a single user: resets backfill progress for one followed author
+// and bumps caching_config_generation so the service re-fetches that author.
+async function refreshCachingUser(pubkey, name) {
+ if (!confirm('Reset backfill progress for ' + (name || pubkey.substring(0, 16) + '…') + '? The caching service will re-download this user\'s events from scratch.')) return;
+ try {
+ const res = await fetch('api/caching.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'reset_user', pubkey: pubkey})
+ });
+ const d = await res.json();
+ if (d.ok) {
+ loadCaching();
+ } else {
+ alert('Refresh failed: ' + (d.error || 'unknown error'));
+ }
+ } catch (e) {
+ console.error('[admin2] refreshCachingUser error:', e);
+ alert('Refresh failed: ' + e.message);
+ }
+}
+
+// ================================
+// DMs
+// ================================
+
+async function loadDMs() {
+ try {
+ const res = await fetch('api/dm.php?limit=50');
+ const d = await res.json();
+ const el = document.getElementById('dm-inbox');
+ if (!d.messages || d.messages.length === 0) {
+ el.innerHTML = 'No messages found.
';
+ return;
+ }
+ el.innerHTML = d.messages.map(m =>
+ `kind ${m.kind} from ${esc((m.pubkey||'').substring(0,16))}… at ${esc(m.created_at)}: ${esc((m.content||'').substring(0,200))}
`
+ ).join('');
+ } catch (e) { console.error('[admin2] dm error:', e); }
+}
+
+// ================================
+// SQL QUERY
+// ================================
+
+async function executeQuery() {
+ const sql = document.getElementById('sql-input').value.trim();
+ if (!sql) { alert('Enter a SQL query'); return; }
+ if (!sql.toUpperCase().startsWith('SELECT')) { alert('Only SELECT queries are allowed'); return; }
+ try {
+ const res = await fetch('api/query.php', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({sql})
+ });
+ const d = await res.json();
+ const info = document.getElementById('query-info');
+ const tableDiv = document.getElementById('query-table');
+ if (d.error) {
+ info.textContent = 'Error: ' + d.error;
+ tableDiv.innerHTML = '';
+ return;
+ }
+ info.textContent = `${d.row_count} rows in ${d.time_ms}ms`;
+ if (d.rows && d.rows.length > 0) {
+ const cols = Object.keys(d.rows[0]);
+ let html = '';
+ cols.forEach(c => html += `${c} `);
+ html += ' ';
+ d.rows.forEach(r => {
+ html += '';
+ cols.forEach(c => html += `${r[c] ?? ''} `);
+ html += ' ';
+ });
+ html += '
';
+ tableDiv.innerHTML = html;
+ } else {
+ tableDiv.innerHTML = 'No rows returned.
';
+ }
+ } catch (e) { console.error('[admin2] query error:', e); }
+}
+
+// ================================
+// DARK MODE
+// ================================
+
+document.getElementById('nav-dark-mode-btn')?.addEventListener('click', () => {
+ document.body.classList.toggle('dark-mode');
+ localStorage.setItem('admin2-dark-mode', document.body.classList.contains('dark-mode'));
+ const btn = document.getElementById('nav-dark-mode-btn');
+ if (btn) btn.textContent = document.body.classList.contains('dark-mode') ? 'LIGHT MODE' : 'DARK MODE';
+});
+
+if (localStorage.getItem('admin2-dark-mode') === 'true') {
+ document.body.classList.add('dark-mode');
+ const btn = document.getElementById('nav-dark-mode-btn');
+ if (btn) btn.textContent = 'LIGHT MODE';
+}
+
+// ================================
+// RELAY LETTER ANIMATION
+// ================================
+
+// Animate the RELAY letters with an underline sweep. Fires on every stats
+// refresh so the user gets a visual cue that the page is updating.
+function startRelayAnimation() {
+ const letters = document.querySelectorAll('.relay-letter');
+ if (letters.length === 0) return;
+
+ // Cancel any in-flight animation so rapid refreshes don't overlap
+ if (relayAnimationTimer) { clearTimeout(relayAnimationTimer); relayAnimationTimer = null; }
+
+ let currentIndex = 0;
+ letters.forEach(l => l.classList.remove('underlined'));
+
+ function animateLetter() {
+ letters.forEach(letter => letter.classList.remove('underlined'));
+ if (letters[currentIndex]) {
+ letters[currentIndex].classList.add('underlined');
+ }
+ currentIndex++;
+ if (currentIndex > letters.length) {
+ // Sweep complete — clear underlines and pause before next refresh
+ letters.forEach(letter => letter.classList.remove('underlined'));
+ relayAnimationTimer = null;
+ return;
+ }
+ relayAnimationTimer = setTimeout(animateLetter, 100);
+ }
+ animateLetter();
+}
+
+// ================================
+// RELAY PUBKEY COPY-TO-CLIPBOARD
+// ================================
+
+document.getElementById('relay-pubkey-container')?.addEventListener('click', async () => {
+ const el = document.getElementById('relay-pubkey');
+ if (!el || !el.textContent.trim()) return;
+ try {
+ await navigator.clipboard.writeText(el.textContent.replace(/\s+/g, ''));
+ const container = document.getElementById('relay-pubkey-container');
+ container.classList.add('copied');
+ setTimeout(() => container.classList.remove('copied'), 500);
+ } catch (e) { console.warn('[admin2] clipboard copy failed:', e); }
+});
+
+// ================================
+// AUTH — nostr_login_lite modal
+// ================================
+
+const loginModal = document.getElementById('login-modal');
+const loginModalContainer = document.getElementById('login-modal-container');
+const profileArea = document.getElementById('profile-area');
+const headerUserName = document.getElementById('header-user-name');
+const headerUserImage = document.getElementById('header-user-image');
+const logoutDropdown = document.getElementById('logout-dropdown');
+
+function showLoginModal() {
+ if (loginModal && loginModalContainer) {
+ if (window.NOSTR_LOGIN_LITE && typeof window.NOSTR_LOGIN_LITE.embed === 'function') {
+ // Clear previous embed before re-embedding
+ loginModalContainer.innerHTML = '';
+ window.NOSTR_LOGIN_LITE.embed('#login-modal-container', { seamless: true });
+ }
+ loginModal.style.display = 'flex';
+ }
+}
+
+function hideLoginModal() {
+ if (loginModal) loginModal.style.display = 'none';
+}
+
+function showProfileInHeader() {
+ if (profileArea) profileArea.style.display = 'flex';
+}
+
+function hideProfileFromHeader() {
+ if (profileArea) profileArea.style.display = 'none';
+}
+
+// Toggle logout dropdown when clicking the profile area
+profileArea?.addEventListener('click', (e) => {
+ // Only toggle if the click wasn't on the logout button itself
+ if (e.target.closest('.logout-btn')) return;
+ if (logoutDropdown) {
+ logoutDropdown.style.display = (logoutDropdown.style.display === 'none' || !logoutDropdown.style.display) ? 'block' : 'none';
+ }
+});
+
+// Hide logout dropdown when clicking elsewhere
+document.addEventListener('click', (e) => {
+ if (logoutDropdown && logoutDropdown.style.display === 'block' && !e.target.closest('#profile-area')) {
+ logoutDropdown.style.display = 'none';
+ }
+});
+
+// Update header profile display from logged-in user's pubkey.
+// Sets a placeholder from the npub immediately, then fetches the kind 0
+// profile event from public relays to populate the name + picture.
+function updateProfileDisplay(pubkey) {
+ if (!pubkey) return;
+ let npub = '';
+ try {
+ if (pubkey.length === 64 && /^[0-9a-fA-F]+$/.test(pubkey)) {
+ npub = window.NostrTools.nip19.npubEncode(pubkey);
+ }
+ } catch (err) { console.warn('[admin2] npub encode failed:', err); }
+
+ // Placeholder until the profile fetch resolves
+ if (headerUserName) {
+ headerUserName.textContent = npub ? npub.substring(0, 16) + '…' : pubkey.substring(0, 16) + '…';
+ }
+ if (headerUserImage) headerUserImage.style.display = 'none';
+
+ // Fetch kind 0 profile from public relays (same approach as original api page)
+ loadUserProfile(pubkey, npub);
+}
+
+// Apply profile data to the header name + profile picture.
+// Uses best_name from the server (resolved per profile_name_preference).
+function applyProfileToHeader(name, picture) {
+ if (headerUserName) headerUserName.textContent = name || 'Anonymous User';
+ if (headerUserImage && picture && typeof picture === 'string' &&
+ (picture.startsWith('http://') || picture.startsWith('https://'))) {
+ headerUserImage.src = picture;
+ headerUserImage.style.display = 'block';
+ headerUserImage.onerror = function() { this.style.display = 'none'; };
+ } else if (headerUserImage) {
+ headerUserImage.style.display = 'none';
+ }
+}
+
+// Fetch the user's profile. Tries the local profiles cache first
+// (admin/api/profile.php), then falls back to public relays if the
+// relay has no cached kind-0 for this pubkey.
+async function loadUserProfile(pubkey, npub) {
+ if (!pubkey) return;
+
+ // Try local profiles cache first.
+ try {
+ const res = await fetch('api/profile.php?pubkey=' + encodeURIComponent(pubkey));
+ if (res.ok) {
+ const profile = await res.json();
+ if (profile && profile.best_name !== undefined) {
+ applyProfileToHeader(profile.best_name, profile.picture);
+ console.log('[admin2] profile loaded from local cache for', profile.best_name);
+ return;
+ }
+ }
+ } catch (e) {
+ // Local endpoint not available — fall through to public relays.
+ }
+
+ // Fall back to public relays.
+ if (!window.NostrTools || !window.NostrTools.SimplePool) {
+ if (headerUserName) headerUserName.textContent = npub ? npub.substring(0, 16) + '…' : 'Anonymous User';
+ return;
+ }
+ const relays = [
+ 'wss://relay.damus.io',
+ 'wss://relay.nostr.band',
+ 'wss://nos.lol',
+ 'wss://relay.primal.net',
+ 'wss://relay.snort.social'
+ ];
+ try {
+ const pool = new window.NostrTools.SimplePool();
+ const timeoutPromise = new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Profile query timeout')), 5000)
+ );
+ const queryPromise = pool.querySync(relays, {
+ kinds: [0],
+ authors: [pubkey],
+ limit: 1
+ });
+ const events = await Promise.race([queryPromise, timeoutPromise]);
+ try { await pool.close(relays); } catch (e) {}
+
+ if (events && events.length > 0) {
+ const profile = JSON.parse(events[0].content);
+ // Use best_name resolution: display_name first, then name.
+ const name = profile.display_name || profile.name || profile.displayName || 'Anonymous User';
+ const picture = profile.picture || profile.image || null;
+ applyProfileToHeader(name, picture);
+ console.log('[admin2] profile loaded from public relays for', name);
+ } else {
+ if (headerUserName) headerUserName.textContent = 'Anonymous User';
+ console.log('[admin2] no profile event found for', pubkey);
+ }
+ } catch (err) {
+ console.warn('[admin2] profile load failed:', err.message);
+ if (headerUserName) headerUserName.textContent = npub ? npub.substring(0, 16) + '…' : 'Error loading profile';
+ }
+}
+
+// Initialize nostr_login_lite and show modal if not already authenticated
+async function initializeAuth() {
+ if (!window.NOSTR_LOGIN_LITE) {
+ console.warn('[admin2] NOSTR_LOGIN_LITE not loaded — skipping auth modal');
+ return;
+ }
+ try {
+ await window.NOSTR_LOGIN_LITE.init({
+ theme: 'default',
+ methods: {
+ extension: true,
+ local: true,
+ seedphrase: true,
+ readonly: true,
+ connect: true,
+ remote: true,
+ otp: false
+ },
+ floatingTab: { enabled: false }
+ });
+ nlLite = window.NOSTR_LOGIN_LITE;
+ console.log('[admin2] nostr_login_lite initialized');
+
+ // Check for existing auth state
+ let alreadyLoggedIn = false;
+ try {
+ const stored = localStorage.getItem('nostr_login_lite_auth');
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ if (parsed && parsed.pubkey) {
+ userPubkey = parsed.pubkey;
+ isLoggedIn = true;
+ alreadyLoggedIn = true;
+ showProfileInHeader();
+ updateProfileDisplay(userPubkey);
+ hideLoginModal();
+ console.log('[admin2] existing auth restored for', userPubkey);
+ }
+ }
+ } catch (e) { /* no stored auth */ }
+
+ if (!alreadyLoggedIn) {
+ console.log('[admin2] no existing auth — showing login modal');
+ showLoginModal();
+ }
+
+ // Listen for auth events
+ window.addEventListener('nlMethodSelected', (event) => {
+ const { pubkey, method, error } = event.detail || {};
+ if (method && pubkey) {
+ userPubkey = pubkey;
+ isLoggedIn = true;
+ console.log('[admin2] login success via', method, pubkey);
+ hideLoginModal();
+ showProfileInHeader();
+ updateProfileDisplay(pubkey);
+ } else if (error) {
+ console.warn('[admin2] auth error:', error);
+ }
+ });
+
+ window.addEventListener('nlLogout', () => {
+ console.log('[admin2] logout event received');
+ userPubkey = null;
+ isLoggedIn = false;
+ hideProfileFromHeader();
+ if (logoutDropdown) logoutDropdown.style.display = 'none';
+ showLoginModal();
+ });
+
+ } catch (err) {
+ console.error('[admin2] nostr_login_lite init failed:', err);
+ }
+}
+
+// Logout function — clears auth state and re-shows login modal
+async function logout() {
+ console.log('[admin2] logging out...');
+ try {
+ if (nlLite && typeof nlLite.logout === 'function') {
+ await nlLite.logout();
+ }
+ } catch (e) { console.warn('[admin2] nlLite.logout error:', e); }
+ userPubkey = null;
+ isLoggedIn = false;
+ hideProfileFromHeader();
+ if (logoutDropdown) logoutDropdown.style.display = 'none';
+ showLoginModal();
+ console.log('[admin2] logged out');
+}
+
+// ================================
+// INIT
+// ================================
+
+switchPage('statistics');
+
+// Start the RELAY animation immediately on page load
+startRelayAnimation();
+
+// Initialize auth + load initial chart on DOM ready
+document.addEventListener('DOMContentLoaded', () => {
+ setTimeout(initializeAuth, 100);
+ // Load the default (hour) chart immediately
+ loadChart(currentChartRange);
+});
diff --git a/admin/assets/index.css b/admin/assets/index.css
new file mode 100644
index 0000000..ffc80ed
--- /dev/null
+++ b/admin/assets/index.css
@@ -0,0 +1,1679 @@
+:root {
+ /* Core Variables (7) */
+ --primary-color: #000000;
+ --secondary-color: #ffffff;
+ --accent-color: #ff0000;
+ --muted-color: #dddddd;
+ --border-color: var(--muted-color);
+ --font-family: "Courier New", Courier, monospace;
+ --border-radius: 5px;
+ --border-width: 1px;
+
+ /* Floating Tab Variables (8) */
+ --tab-bg-logged-out: #ffffff;
+ --tab-bg-logged-in: #ffffff;
+ --tab-bg-opacity-logged-out: 0.9;
+ --tab-bg-opacity-logged-in: 0.2;
+ --tab-color-logged-out: #000000;
+ --tab-color-logged-in: #ffffff;
+ --tab-border-logged-out: #000000;
+ --tab-border-logged-in: #ff0000;
+ --tab-border-opacity-logged-out: 1.0;
+ --tab-border-opacity-logged-in: 0.1;
+}
+
+/* Dark Mode Overrides */
+body.dark-mode {
+ --primary-color: #ffffff;
+ --secondary-color: #000000;
+ --accent-color: #ff0000;
+ --muted-color: #222222;
+ --border-color: var(--muted-color);
+
+
+ --tab-bg-logged-out: #000000;
+ --tab-color-logged-out: #ffffff;
+ --tab-border-logged-out: #ffffff;
+ --tab-bg-logged-in: #000000;
+ --tab-color-logged-in: #ffffff;
+ --tab-border-logged-in: #00ffff;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: var(--font-family);
+ background-color: var(--secondary-color);
+ color: var(--primary-color);
+ /* line-height: 1.4; */
+ padding: 0;
+ max-width: none;
+ margin: 0;
+}
+
+/* Header Styles */
+.main-header {
+ background-color: var(--secondary-color);
+
+ padding: 15px 20px;
+ z-index: 100;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+}
+
+.header-title {
+ margin: 0;
+ font-size: 24px;
+ font-weight: normal;
+ color: var(--primary-color);
+ border: none;
+ padding: 0;
+ text-align: left;
+}
+
+.relay-info {
+ text-align: center;
+ flex: 1;
+ max-width: 150px;
+ margin: 0 auto;
+}
+
+.relay-name {
+ font-size: 14px;
+ font-weight: bold;
+ color: var(--primary-color);
+ margin-bottom: 2px;
+}
+
+.relay-pubkey-container {
+ border: 1px solid transparent;
+ border-radius: var(--border-radius);
+ padding: 4px;
+ margin-top: 4px;
+ cursor: pointer;
+ transition: border-color 0.2s ease;
+ background-color: var(--secondary-color);
+ display: inline-block;
+ width: fit-content;
+}
+
+.relay-pubkey-container:hover {
+ border-color: var(--border-color);
+}
+
+.relay-pubkey-container.copied {
+ border-color: var(--accent-color);
+ animation: flash-accent 0.5s ease-in-out;
+}
+
+.relay-pubkey {
+ font-size: 8px;
+ color: var(--primary-color);
+ font-family: "Courier New", Courier, monospace;
+ line-height: 1.2;
+ white-space: pre-line;
+ text-align: center;
+}
+
+@keyframes flash-accent {
+ 0% { border-color: var(--accent-color); }
+ 50% { border-color: var(--accent-color); }
+ 100% { border-color: transparent; }
+}
+
+.relay-description {
+ font-size: 10px;
+ color: var(--primary-color);
+ margin-bottom: 0;
+ display: inline-block;
+ width: fit-content;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+}
+
+.header-title {
+ margin: 0;
+ font-size: 24px;
+ font-weight: bolder;
+ color: var(--primary-color);
+ border: none;
+ padding: 0;
+ text-align: left;
+ display: flex;
+ gap: 2px;
+}
+
+.relay-letter {
+ position: relative;
+ display: inline-block;
+ transition: all 0.05s ease;
+}
+
+.relay-letter.underlined::after {
+ content: '';
+ position: absolute;
+ bottom: -2px;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background-color: var(--accent-color);
+}
+
+.header-user-name {
+ display: block;
+ font-weight: 500;
+ color: var(--primary-color);
+ font-size: 10px;
+ text-align: center;
+ margin-top: 4px;
+}
+
+.profile-area {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ cursor: pointer;
+ padding: 8px 12px;
+ border-radius: var(--border-radius);
+ transition: background-color 0.2s ease;
+ /* margin-left: auto; */
+}
+
+.admin-label {
+ font-size: 10px;
+ color: var(--primary-color);
+ font-weight: normal;
+ margin-bottom: 4px;
+ text-align: center;
+}
+
+.profile-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+}
+
+.profile-area:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.profile-info {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.header-user-image {
+ width: 48px; /* 50% larger than 32px */
+ height: 48px; /* 50% larger than 32px */
+ border-radius: var(--border-radius); /* Curved corners like other elements */
+ object-fit: cover;
+ border: 2px solid transparent; /* Invisible border */
+ background-color: var(--secondary-color);
+}
+
+
+.logout-dropdown {
+ position: absolute;
+ top: 100%;
+ right: 0;
+ background-color: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ min-width: 120px;
+ z-index: 200;
+ margin-top: 4px;
+}
+
+.logout-btn {
+ width: 100%;
+ padding: 5px 10px;
+ background: none;
+ border: none;
+ color: var(--primary-color);
+ text-align: left;
+ cursor: pointer;
+ font-size: 10px;
+ font-family: var(--font-family);
+ border-radius: var(--border-radius);
+ transition: background-color 0.2s ease;
+}
+
+.logout-btn:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+/* Login Modal Styles */
+.login-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.login-modal-content {
+ background-color: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 30px;
+ max-width: 400px;
+ width: 90%;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+}
+
+h1 {
+ border-bottom: var(--border-width) solid var(--border-color);
+ padding-bottom: 10px;
+ margin-bottom: 30px;
+ font-weight: bold;
+ font-size: 24px;
+ font-family: var(--font-family);
+ color: var(--primary-color);
+}
+
+h2 {
+ font-weight: normal;
+ text-align: center;
+ font-size: 16px;
+ font-family: var(--font-family);
+ color: var(--primary-color);
+}
+
+h3 {
+ font-weight: normal;
+ font-size: 12px;
+ font-family: var(--font-family);
+ color: var(--primary-color);
+ padding-bottom: 10px;
+}
+
+label {
+ display: block;
+ margin-bottom: 5px;
+ font-weight: lighter;
+ font-size: 10px;
+ font-family: var(--font-family);
+ color: var(--primary-color);
+}
+
+
+
+
+.section {
+ background: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 20px;
+ margin-bottom: 20px;
+ margin-left: 5px;
+ margin-right:5px;
+}
+
+.section-header {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding-bottom: 15px;
+}
+
+
+
+
+
+.input-group {
+ margin-bottom: 15px;
+}
+
+
+input,
+textarea,
+select {
+ width: 100%;
+ padding: 8px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 14px;
+ box-sizing: border-box;
+ transition: all 0.2s ease;
+}
+
+input:focus,
+textarea:focus,
+select:focus {
+ border-color: var(--accent-color);
+ outline: none;
+}
+
+button {
+ width: 100%;
+ padding: 8px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 14px;
+ cursor: pointer;
+ margin: 5px 0;
+ font-weight: bold;
+ transition: all 0.2s ease;
+}
+
+button:hover {
+ border-color: var(--accent-color);
+}
+
+button:active {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+}
+
+button:disabled {
+ background-color: var(--muted-color);
+ color: var(--primary-color);
+ cursor: not-allowed;
+ border-color: var(--muted-color);
+}
+
+/* Flash animation for refresh button */
+@keyframes flash-red {
+ 0% { border-color: var(--border-color); }
+ 50% { border-color: var(--accent-color); }
+ 100% { border-color: var(--border-color); }
+}
+
+.flash-red {
+ animation: flash-red 1s ease-in-out;
+}
+
+/* Flash animation for updated statistics values */
+@keyframes flash-value {
+ 0% { color: var(--primary-color); }
+ 50% { color: var(--accent-color); }
+ 100% { color: var(--primary-color); }
+}
+
+.flash-value {
+ animation: flash-value 1s ease-in-out;
+}
+
+/* Npub links styling */
+.npub-link {
+ color: var(--primary-color);
+ text-decoration: none;
+ font-weight: normal;
+ transition: color 0.2s ease;
+}
+
+.npub-link:hover {
+ color: var(--accent-color);
+}
+
+.status {
+ padding: 10px;
+ margin: 10px 0;
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-weight: bold;
+ font-family: var(--font-family);
+ transition: all 0.2s ease;
+}
+
+.status.connected {
+ background-color: var(--primary-color);
+ color: var(--secondary-color);
+}
+
+.status.disconnected {
+ background-color: var(--secondary-color);
+ color: var(--primary-color);
+}
+
+.status.authenticated {
+ background-color: var(--primary-color);
+ color: var(--secondary-color);
+}
+
+.status.error {
+ background-color: var(--secondary-color);
+ color: var(--primary-color);
+ border-color: var(--accent-color);
+}
+
+
+.config-table {
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+ margin: 10px 0;
+ overflow: hidden;
+}
+
+.config-table th,
+.config-table td {
+ border: 0.1px solid var(--muted-color);
+ padding: 4px;
+ text-align: left;
+ font-family: var(--font-family);
+ font-size: 10px;
+}
+
+.config-table tbody tr:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.config-table-container {
+ overflow-x: auto;
+ max-width: 100%;
+}
+
+.config-table th {
+ font-weight: bold;
+ height: 24px; /* Base height for tbody rows */
+ line-height: 24px; /* Center text vertically */
+}
+
+.config-table td {
+ height: 16px; /* 50% taller than tbody rows would be */
+ line-height: 16px; /* Center text vertically */
+}
+
+/* Inline config value inputs - remove borders and padding to fit seamlessly in table cells */
+.config-value-input {
+ border: none;
+ padding: 2px 4px;
+ background: transparent;
+ width: 100%;
+ min-height: auto;
+ font-family: inherit;
+ font-size: inherit;
+ color: inherit;
+ border-radius: 0;
+}
+
+/* Relay Events Styles */
+.status-message {
+ margin-top: 10px;
+ padding: 8px;
+ border-radius: var(--border-radius);
+ font-size: 14px;
+ font-family: var(--font-family);
+ text-align: center;
+}
+
+.relay-entry {
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 10px;
+ margin-bottom: 10px;
+ background: var(--secondary-color);
+}
+
+.config-value-input:focus {
+ border: 1px solid var(--accent-color);
+ background: var(--secondary-color);
+ outline: none;
+}
+
+/* Config actions cell - clickable for saving */
+.config-actions-cell {
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-align: center !important;
+ font-weight: bold;
+ vertical-align: middle;
+ width: 60px;
+ min-width: 60px;
+ max-width: 60px;
+ padding: 8px 4px;
+}
+
+.config-actions-cell:hover {
+ border: 1px solid var(--accent-color);
+ background-color: var(--muted-color);
+}
+
+.json-display {
+ background-color: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 10px;
+ font-family: var(--font-family);
+ font-size: 12px;
+ white-space: pre-wrap;
+ max-height: 300px;
+ overflow-y: auto;
+ margin: 10px 0;
+}
+
+.log-panel {
+ height: 200px;
+ overflow-y: auto;
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 10px;
+ font-size: 12px;
+ background-color: var(--secondary-color);
+ font-family: var(--font-family);
+}
+
+.log-entry {
+ margin-bottom: 5px;
+ border-bottom: 1px solid var(--muted-color);
+ padding-bottom: 5px;
+}
+
+.log-timestamp {
+ font-weight: bold;
+ font-family: var(--font-family);
+}
+
+.inline-buttons {
+ display: flex;
+ gap: 10px;
+ flex-wrap: nowrap;
+}
+
+.inline-buttons button {
+ flex: 1;
+}
+
+.user-info {
+ padding: 10px;
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ margin: 10px 0;
+ background-color: var(--secondary-color);
+}
+
+.user-info-container {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+}
+
+.user-details {
+ order: -1; /* Show user details first when logged in */
+}
+
+.login-section {
+ text-align: center;
+}
+
+.logout-section {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.login-logout-btn {
+ width: auto;
+ min-width: 120px;
+ padding: 12px 16px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 14px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ margin: 0;
+ flex-shrink: 0;
+}
+
+.login-logout-btn:hover {
+ border-color: var(--accent-color);
+}
+
+.login-logout-btn:active {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+}
+
+.login-logout-btn.logout-state {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+ border-color: var(--accent-color);
+}
+
+.login-logout-btn.logout-state:hover {
+ background: var(--primary-color);
+ border-color: var(--border-color);
+}
+
+.user-pubkey {
+ font-family: var(--font-family);
+ font-size: 12px;
+ word-break: break-all;
+ margin: 5px 0;
+}
+
+/* User profile header with image */
+.user-profile-header {
+ display: flex;
+ align-items: flex-start;
+ gap: 15px;
+}
+
+.user-image-container {
+ flex-shrink: 0;
+}
+
+.user-profile-image {
+ width: 60px;
+ height: 60px;
+ border-radius: var(--border-radius);
+ object-fit: cover;
+ border: 2px solid var(--border-color);
+ background-color: var(--bg-color);
+}
+
+.user-text-info {
+ flex: 1;
+ min-width: 0; /* Allow text to wrap */
+}
+
+.hidden {
+ display: none;
+}
+
+
+
+.countdown-btn {
+ width: auto;
+ min-width: 40px;
+ padding: 8px 12px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 10px;
+ /* font-weight: bold; */
+ cursor: pointer;
+ transition: all 0.2s ease;
+ margin-left: auto;
+ position: relative;
+}
+
+.countdown-btn:hover::after {
+ content: "countdown";
+ position: absolute;
+ top: -30px;
+ left: 50%;
+ transform: translateX(-50%);
+ background: var(--primary-color);
+ color: var(--secondary-color);
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: normal;
+ white-space: nowrap;
+ z-index: 1000;
+ border: 1px solid var(--border-color);
+}
+
+.auth-rules-controls {
+ margin-bottom: 15px;
+}
+
+.section-header .status {
+ margin: 0;
+ padding: 5px 10px;
+ min-width: auto;
+ font-size: 12px;
+}
+
+/* Auth Rule Input Sections Styling */
+.auth-rule-section {
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 15px;
+ margin: 15px 0;
+ background-color: var(--secondary-color);
+}
+
+.auth-rule-section h3 {
+ margin: 0 0 10px 0;
+ font-size: 14px;
+ font-weight: bold;
+ border-left: 4px solid var(--border-color);
+ padding-left: 8px;
+ font-family: var(--font-family);
+ color: var(--primary-color);
+}
+
+.auth-rule-section p {
+ margin: 0 0 15px 0;
+ font-size: 13px;
+ color: var(--muted-color);
+ font-family: var(--font-family);
+}
+
+.rule-status {
+ margin-top: 10px;
+ padding: 8px;
+ border: var(--border-width) solid var(--muted-color);
+ border-radius: var(--border-radius);
+ font-size: 12px;
+ min-height: 20px;
+ background-color: var(--secondary-color);
+ font-family: var(--font-family);
+ transition: all 0.2s ease;
+}
+
+.rule-status.success {
+ border-color: #4CAF50;
+ background-color: #E8F5E8;
+ color: #2E7D32;
+}
+
+.rule-status.error {
+ border-color: var(--accent-color);
+ background-color: #FFEBEE;
+ color: #C62828;
+}
+
+.rule-status.warning {
+ border-color: #FF9800;
+ background-color: #FFF3E0;
+ color: #E65100;
+}
+
+.warning-box {
+ border: var(--border-width) solid #FF9800;
+ border-radius: var(--border-radius);
+ background-color: #FFF3E0;
+ padding: 10px;
+ margin: 10px 0;
+ font-size: 13px;
+ color: #E65100;
+ font-family: var(--font-family);
+}
+
+.warning-box strong {
+ color: #D84315;
+}
+
+#login-section {
+ text-align: center;
+ padding: 20px;
+}
+
+/* Floating tab styles */
+.floating-tab {
+ font-family: var(--font-family);
+ border-radius: var(--border-radius);
+ border: var(--border-width) solid;
+ transition: all 0.2s ease;
+}
+
+.floating-tab--logged-out {
+ background: rgba(255, 255, 255, var(--tab-bg-opacity-logged-out));
+ color: var(--tab-color-logged-out);
+ border-color: rgba(0, 0, 0, var(--tab-border-opacity-logged-out));
+}
+
+.floating-tab--logged-in {
+ background: rgba(0, 0, 0, var(--tab-bg-opacity-logged-in));
+ color: var(--tab-color-logged-in);
+ border-color: rgba(255, 0, 0, var(--tab-border-opacity-logged-in));
+}
+
+.transition {
+ transition: all 0.2s ease;
+}
+
+/* SQL Query Interface Styles */
+.query-selector {
+ margin-bottom: 15px;
+}
+
+.query-selector select {
+ width: 100%;
+ padding: 8px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 14px;
+ cursor: pointer;
+}
+
+.query-selector select:focus {
+ border-color: var(--accent-color);
+ outline: none;
+}
+
+.query-selector optgroup {
+ font-weight: bold;
+ color: var(--primary-color);
+}
+
+.query-selector option {
+ padding: 4px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+}
+
+.query-editor textarea {
+ width: 100%;
+ min-height: 120px;
+ resize: vertical;
+ font-family: "Courier New", Courier, monospace;
+ font-size: 12px;
+ line-height: 1.4;
+ tab-size: 4;
+ white-space: pre;
+}
+
+.query-actions {
+ display: flex;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.query-actions button {
+ flex: 1;
+ min-width: 120px;
+}
+
+.primary-button {
+ background: var(--primary-color);
+ color: var(--secondary-color);
+ border-color: var(--primary-color);
+}
+
+.primary-button:hover {
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border-color: var(--accent-color);
+}
+
+.danger-button {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+ border-color: var(--accent-color);
+}
+
+.danger-button:hover {
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border-color: var(--accent-color);
+}
+
+.query-info {
+ padding: 10px;
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ margin: 10px 0;
+ font-family: var(--font-family);
+ font-size: 12px;
+ background-color: var(--secondary-color);
+}
+
+.query-info-success {
+ border-color: #4CAF50;
+ background-color: #E8F5E8;
+ color: #2E7D32;
+}
+
+.query-info-success span {
+ display: inline-block;
+ margin-right: 15px;
+}
+
+.request-id {
+ font-family: "Courier New", Courier, monospace;
+ font-size: 10px;
+ opacity: 0.7;
+}
+
+.error-message {
+ border-color: var(--accent-color);
+ background-color: #FFEBEE;
+ color: #C62828;
+ padding: 10px;
+ border-radius: var(--border-radius);
+ margin: 10px 0;
+ font-family: var(--font-family);
+ font-size: 12px;
+}
+
+.sql-results-table {
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+ margin: 10px 0;
+ overflow: hidden;
+ font-size: 11px;
+}
+
+.sql-results-table th,
+.sql-results-table td {
+ border: 0.1px solid var(--muted-color);
+ padding: 6px 8px;
+ text-align: left;
+ font-family: var(--font-family);
+ white-space: nowrap;
+ min-width: 100px;
+}
+
+.sql-results-table th {
+ font-weight: bold;
+ background-color: rgba(0, 0, 0, 0.05);
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+
+.sql-results-table tbody tr:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.sql-results-table tbody tr:nth-child(even) {
+ background-color: rgba(0, 0, 0, 0.02);
+}
+
+.no-results {
+ text-align: center;
+ font-style: italic;
+ color: var(--muted-color);
+ padding: 20px;
+ font-family: var(--font-family);
+}
+
+.loading {
+ text-align: center;
+ font-style: italic;
+ color: var(--muted-color);
+ padding: 20px;
+ font-family: var(--font-family);
+}
+
+/* Dark mode adjustments for SQL interface */
+body.dark-mode .query-info-success {
+ border-color: #4CAF50;
+ background-color: rgba(76, 175, 80, 0.1);
+ color: #81C784;
+}
+
+body.dark-mode .error-message {
+ border-color: var(--accent-color);
+ background-color: rgba(244, 67, 54, 0.1);
+ color: #EF5350;
+}
+
+body.dark-mode .sql-results-table th {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+body.dark-mode .sql-results-table tbody tr:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+body.dark-mode .sql-results-table tbody tr:nth-child(even) {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+
+/* Config Toggle Button Styles */
+.config-toggle-btn {
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ background: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 14px;
+ cursor: pointer;
+ margin-left: 10px;
+ font-weight: bold;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* Toggle Button Styles */
+.toggle-btn {
+ width: auto;
+ min-width: 120px;
+ padding: 8px 12px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 12px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ margin-left: auto;
+}
+
+.toggle-btn:hover {
+ border-color: var(--accent-color);
+}
+
+.toggle-btn:active {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+}
+
+.config-toggle-btn:hover {
+ border-color: var(--accent-color);
+}
+
+.config-toggle-btn:active {
+ background: var(--accent-color);
+ color: var(--secondary-color);
+}
+
+.config-toggle-btn[data-state="true"] {
+ color: var(--accent-color);
+}
+
+.config-toggle-btn[data-state="false"] {
+ color: var(--primary-color);
+}
+
+.config-toggle-btn[data-state="indeterminate"] {
+ background-color: var(--muted-color);
+ color: var(--primary-color);
+ cursor: not-allowed;
+ border-color: var(--muted-color);
+
+}
+
+
+/* ================================
+ REAL-TIME EVENT RATE CHART
+ ================================ */
+
+.chart-container {
+ margin: 20px 0;
+ padding: 15px;
+ background: var(--secondary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+}
+
+/* Chart range selector tabs (1H / 1D / 1M / 1Y) */
+.chart-range-tabs {
+ display: flex;
+ gap: 4px;
+ margin-bottom: 8px;
+}
+
+.chart-tab {
+ padding: 4px 12px;
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-family: var(--font-family);
+ font-size: 11px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+}
+
+.chart-tab:hover {
+ border-color: var(--primary-color);
+}
+
+.chart-tab.active {
+ background: var(--secondary-color);
+ color: var(--primary-color);
+ border-color: var(--primary-color);
+ font-weight: bold;
+}
+
+/* Dim inactive tabs */
+.chart-tab:not(.active) {
+ color: var(--muted-color);
+ border-color: var(--muted-color);
+}
+
+#event-rate-chart {
+ font-family: var(--font-family);
+ font-size: 12px;
+ line-height: 1.2;
+ color: var(--primary-color);
+ background: var(--secondary-color);
+ padding: 20px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: pre;
+ border: var(--border-width) solid var(--border-color);
+ border-radius: var(--border-radius);
+ box-sizing: border-box;
+}
+
+/* ================================
+ SIDE NAVIGATION MENU
+ ================================ */
+
+.side-nav {
+ position: fixed;
+ top: 0;
+ left: -300px;
+ width: 280px;
+ height: 100vh;
+ background: var(--secondary-color);
+ border-right: var(--border-width) solid var(--border-color);
+ z-index: 1000;
+ transition: left 0.3s ease;
+ overflow-y: auto;
+ padding-top: 80px;
+}
+
+.side-nav.open {
+ left: 0;
+}
+
+.side-nav-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 999;
+ display: none;
+}
+
+.side-nav-overlay.show {
+ display: block;
+}
+
+.nav-menu {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.nav-menu li {
+ border-bottom: var(--border-width) solid var(--muted-color);
+}
+
+.nav-menu li:last-child {
+ border-bottom: none;
+}
+
+.nav-item {
+ display: block;
+ padding: 15px 20px;
+ color: var(--primary-color);
+ text-decoration: none;
+ font-family: var(--font-family);
+ font-size: 16px;
+ font-weight: bold;
+ transition: all 0.2s ease;
+ cursor: pointer;
+ border: 2px solid var(--secondary-color);
+ background: none;
+ width: 100%;
+ text-align: left;
+}
+
+.nav-item:hover {
+ border: 2px solid var(--secondary-color);
+ background:var(--muted-color);
+ color: var(--accent-color);
+}
+
+.nav-item.active {
+ text-decoration: underline;
+ padding-left: 16px;
+ }
+
+.nav-footer {
+ position: absolute;
+ bottom: 20px;
+ left: 0;
+ right: 0;
+ padding: 0 20px;
+ }
+
+.nav-footer-btn {
+ display: block;
+ width: 100%;
+ padding: 12px 20px;
+ margin-bottom: 8px;
+ color: var(--primary-color);
+
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ font-family: var(--font-family);
+ font-size: 14px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ }
+
+.nav-footer-btn:hover {
+ background:var(--muted-color);
+ border-color: var(--accent-color);
+ }
+
+.nav-footer-btn:last-child {
+ margin-bottom: 0;
+ }
+
+.header-title.clickable {
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.header-title.clickable:hover {
+ opacity: 0.8;
+}
+
+/* ================================
+ SUBSCRIPTION TABLE COLLAPSIBLE GROUPS
+ ================================ */
+
+/* Subscription group header styles */
+.subscription-group-header {
+
+ font-weight: 500;
+
+ cursor: pointer;
+ user-select: none;
+}
+
+.subscription-group-header:hover {
+ background-color: var(--secondary-color);
+}
+
+.expand-icon {
+ display: inline-block;
+ width: 20px;
+ transition: transform 0.2s ease;
+ font-size: 12px;
+}
+
+/* Detail row styles */
+.subscription-detail-row {
+ /* background-color: var(--secondary-color); */
+}
+
+/* ================================
+ WEB OF TRUST (WoT) STYLES
+ ================================ */
+
+.wot-status-row, .wot-stats-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 0;
+ font-size: 14px;
+}
+
+.wot-indicator {
+ padding: 2px 10px;
+ border-radius: 4px;
+ font-weight: bold;
+ font-size: 12px;
+}
+
+.wot-indicator.wot-found { background: #28a745; color: white; }
+.wot-indicator.wot-missing { background: #dc3545; color: white; }
+.wot-indicator.wot-unknown { background: #6c757d; color: white; }
+
+.wot-level-selector { padding: 10px 0; }
+.wot-level-selector label { display: block; margin-bottom: 5px; font-weight: bold; }
+
+.wot-level-btn { min-width: 100px; }
+.wot-level-btn.active {
+ background: var(--accent-color, #ff0000);
+ color: white;
+ border-color: var(--accent-color, #ff0000);
+}
+
+.wot-level-description {
+ font-size: 12px;
+ color: var(--primary-color);
+ margin-top: 5px;
+ font-style: italic;
+ opacity: 0.8;
+}
+
+/* Dark mode adjustments for WoT */
+body.dark-mode .wot-indicator.wot-found { background: #28a745; }
+body.dark-mode .wot-indicator.wot-missing { background: #dc3545; }
+body.dark-mode .wot-indicator.wot-unknown { background: #6c757d; }
+
+/* ================================
+ ADMIN ACCESS GATE STYLES
+ ================================ */
+
+/* Access Denied Overlay */
+.access-denied-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.85);
+ z-index: 9999;
+ display: none;
+ justify-content: center;
+ align-items: center;
+}
+
+.access-denied-content {
+ background: var(--card-bg);
+ border: 2px solid #dc3545;
+ border-radius: 12px;
+ padding: 40px 60px;
+ text-align: center;
+ max-width: 500px;
+ box-shadow: 0 10px 40px rgba(220, 53, 69, 0.3);
+}
+
+.access-denied-icon {
+ font-size: 64px;
+ margin-bottom: 20px;
+}
+
+.access-denied-content h2 {
+ color: #dc3545;
+ font-size: 32px;
+ margin-bottom: 20px;
+ letter-spacing: 2px;
+}
+
+.access-denied-message {
+ font-size: 16px;
+ color: var(--primary-color);
+ margin-bottom: 10px;
+ line-height: 1.5;
+}
+
+.access-denied-submessage {
+ font-size: 14px;
+ color: var(--muted-color);
+ margin-bottom: 30px;
+ font-style: italic;
+}
+
+.access-denied-logout-btn {
+ background: #dc3545;
+ color: white;
+ border: none;
+ padding: 12px 40px;
+ font-size: 16px;
+ font-weight: bold;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.access-denied-logout-btn:hover {
+ background: #c82333;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4);
+}
+
+/* Admin Verification Loading Overlay */
+.admin-verification-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 9998;
+ display: none;
+ justify-content: center;
+ align-items: center;
+}
+
+.admin-verification-content {
+ background: var(--card-bg);
+ border: 1px solid var(--border-color);
+ border-radius: 12px;
+ padding: 40px 60px;
+ text-align: center;
+ max-width: 450px;
+}
+
+.admin-verification-content h3 {
+ color: var(--accent-color);
+ font-size: 20px;
+ margin-bottom: 15px;
+}
+
+.admin-verification-content p {
+ color: var(--muted-color);
+ font-size: 14px;
+ margin-top: 15px;
+}
+
+/* Spinner Animation */
+.spinner {
+ width: 50px;
+ height: 50px;
+ border: 4px solid var(--border-color);
+ border-top: 4px solid var(--accent-color);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto 20px;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* Dark mode adjustments */
+body.dark-mode .access-denied-content {
+ background: var(--card-bg);
+}
+
+body.dark-mode .admin-verification-content {
+ background: var(--card-bg);
+}
+
+.subscription-detail-row:hover {
+ background-color: var(--muted-color);
+}
+
+/* Detail row cell styles */
+.subscription-detail-prefix {
+ padding-left: 30px;
+ font-family: 'Courier New', monospace;
+ font-size: 11px;
+ color: var(--muted-color);
+}
+
+.subscription-detail-id {
+ font-family: 'Courier New', monospace;
+ font-size: 12px;
+}
+
+
+/* ================================
+ IP BANS TABLE - compact rows
+ ================================ */
+#ip-bans-table td, #ip-bans-table th {
+ padding: 4px 8px;
+ line-height: 1.3;
+ font-size: 13px;
+}
+#ip-bans-table button {
+ padding: 2px 8px;
+ font-size: 12px;
+}
+
+/* =====================================================================
+ Profile name rendering hardening.
+ Handles hostile characters in user-controlled names: bidi overrides,
+ Zalgo/stacked combining marks, zero-width chars, newlines, and
+ very long names. The data is stored verbatim; these rules contain
+ the visual impact without altering the value.
+ ===================================================================== */
+
+/* bdi isolates bidi text so U+202E (RTL override) in a name cannot
+ reverse surrounding table content. Applied via in app.js. */
+bdi {
+ unicode-bidi: isolate;
+}
+
+/* Name cells in tables: prevent layout breakage. */
+#stats-pubkeys-table td:nth-child(2),
+#caching-follows-table td:nth-child(1) {
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ line-height: 1.4;
+ vertical-align: middle;
+}
+
+/* Header user name: contain overflow from Zalgo/long names. */
+.header-user-name {
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Name-field usage stats panel */
+.name-usage-stats {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ align-items: center;
+ padding: 8px 12px;
+ font-size: 12px;
+ color: var(--text-color);
+}
+.name-usage-label {
+ font-weight: bold;
+}
+.name-usage-item {
+ padding: 2px 8px;
+ border-radius: var(--border-radius);
+ background: var(--bg-color);
+ border: 1px solid var(--border-color);
+}
+.name-usage-differ {
+ font-weight: bold;
+ color: var(--accent-color);
+}
+
+/* Caching follows table: expandable per-relay detail rows */
+.follows-row {
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.follows-row:hover {
+ background: var(--hover-bg, rgba(255,255,255,0.05));
+}
+.follows-detail td {
+ padding: 8px 12px;
+ background: var(--bg-color);
+ border-top: none;
+}
+.relay-progress-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px 16px;
+ padding: 4px 0;
+}
+.relay-progress-row {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 12px;
+ white-space: nowrap;
+}
+.relay-icon {
+ font-size: 14px;
+}
+.relay-url {
+ color: var(--text-color);
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.relay-events {
+ color: var(--text-muted, #888);
+ font-size: 11px;
+}
+.relay-status {
+ font-size: 10px;
+ padding: 1px 6px;
+ border-radius: 3px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+.relay-done {
+ background: rgba(40, 167, 69, 0.2);
+ color: #28a745;
+ border: 1px solid rgba(40, 167, 69, 0.3);
+}
+.relay-pending {
+ background: rgba(255, 193, 7, 0.2);
+ color: #ffc107;
+ border: 1px solid rgba(255, 193, 7, 0.3);
+}
+
+/* Caching: per-user refresh button in detail row */
+.follows-detail-content {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ gap: 8px 16px;
+}
+.follows-detail-actions {
+ margin-left: auto;
+}
+.refresh-user-btn {
+ font-size: 12px;
+ padding: 4px 12px;
+ cursor: pointer;
+ background: var(--accent-color, #007bff);
+ color: #fff;
+ border: none;
+ border-radius: var(--border-radius, 4px);
+ transition: opacity 0.15s;
+}
+.refresh-user-btn:hover {
+ opacity: 0.85;
+}
+.refresh-user-btn:active {
+ opacity: 0.7;
+}
diff --git a/admin/assets/nostr-lite.js b/admin/assets/nostr-lite.js
new file mode 100644
index 0000000..c6773cf
--- /dev/null
+++ b/admin/assets/nostr-lite.js
@@ -0,0 +1,4282 @@
+/**
+ * NOSTR_LOGIN_LITE - Authentication Library
+ *
+ * ⚠️ WARNING: THIS FILE IS AUTO-GENERATED - DO NOT EDIT MANUALLY!
+ * ⚠️ To make changes, edit lite/build.js and run: cd lite && node build.js
+ * ⚠️ Any manual edits to this file will be OVERWRITTEN when build.js runs!
+ *
+ * Two-file architecture:
+ * 1. Load nostr.bundle.js (official nostr-tools bundle)
+ * 2. Load nostr-lite.js (this file - NOSTR_LOGIN_LITE library with CSS-only themes)
+ * Generated on: 2025-10-01T14:18:10.269Z
+ */
+
+// Verify dependencies are loaded
+if (typeof window !== 'undefined') {
+ if (!window.NostrTools) {
+ console.error('NOSTR_LOGIN_LITE: nostr.bundle.js must be loaded first');
+ throw new Error('Missing dependency: nostr.bundle.js');
+ }
+
+ console.log('NOSTR_LOGIN_LITE: Dependencies verified ✓');
+ console.log('NOSTR_LOGIN_LITE: NostrTools available with keys:', Object.keys(window.NostrTools));
+ console.log('NOSTR_LOGIN_LITE: NIP-06 available:', !!window.NostrTools.nip06);
+ console.log('NOSTR_LOGIN_LITE: NIP-46 available:', !!window.NostrTools.nip46);
+}
+
+// ======================================
+// NOSTR_LOGIN_LITE Components
+// ======================================
+
+// ======================================
+// CSS-Only Theme System
+// ======================================
+
+const THEME_CSS = {
+ 'default': `/**
+ * NOSTR_LOGIN_LITE - Default Monospace Theme
+ * Black/white/red color scheme with monospace typography
+ * Simplified 14-variable system (6 core + 8 floating tab)
+ */
+
+:root {
+ /* Core Variables (6) */
+ --nl-primary-color: #000000;
+ --nl-secondary-color: #ffffff;
+ --nl-accent-color: #ff0000;
+ --nl-muted-color: #CCCCCC;
+ --nl-font-family: "Courier New", Courier, monospace;
+ --nl-border-radius: 15px;
+ --nl-border-width: 3px;
+
+ /* Floating Tab Variables (8) */
+ --nl-tab-bg-logged-out: #ffffff;
+ --nl-tab-bg-logged-in: #ffffff;
+ --nl-tab-bg-opacity-logged-out: 0.9;
+ --nl-tab-bg-opacity-logged-in: 0.2;
+ --nl-tab-color-logged-out: #000000;
+ --nl-tab-color-logged-in: #ffffff;
+ --nl-tab-border-logged-out: #000000;
+ --nl-tab-border-logged-in: #ff0000;
+ --nl-tab-border-opacity-logged-out: 1.0;
+ --nl-tab-border-opacity-logged-in: 0.1;
+}
+
+/* Base component styles using simplified variables */
+.nl-component {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+.nl-button {
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ font-family: var(--nl-font-family);
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.nl-button:hover {
+ border-color: var(--nl-accent-color);
+}
+
+.nl-button:active {
+ background: var(--nl-accent-color);
+ color: var(--nl-secondary-color);
+}
+
+.nl-input {
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ font-family: var(--nl-font-family);
+ box-sizing: border-box;
+}
+
+.nl-input:focus {
+ border-color: var(--nl-accent-color);
+ outline: none;
+}
+
+.nl-container {
+ background: var(--nl-secondary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+}
+
+.nl-title, .nl-heading {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+ margin: 0;
+}
+
+.nl-text {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+.nl-text--muted {
+ color: var(--nl-muted-color);
+}
+
+.nl-icon {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+/* Floating tab styles */
+.nl-floating-tab {
+ font-family: var(--nl-font-family);
+ border-radius: var(--nl-border-radius);
+ border: var(--nl-border-width) solid;
+ transition: all 0.2s ease;
+}
+
+.nl-floating-tab--logged-out {
+ background: rgba(255, 255, 255, var(--nl-tab-bg-opacity-logged-out));
+ color: var(--nl-tab-color-logged-out);
+ border-color: rgba(0, 0, 0, var(--nl-tab-border-opacity-logged-out));
+}
+
+.nl-floating-tab--logged-in {
+ background: rgba(0, 0, 0, var(--nl-tab-bg-opacity-logged-in));
+ color: var(--nl-tab-color-logged-in);
+ border-color: rgba(255, 0, 0, var(--nl-tab-border-opacity-logged-in));
+}
+
+.nl-transition {
+ transition: all 0.2s ease;
+}`,
+ 'dark': `/**
+ * NOSTR_LOGIN_LITE - Dark Monospace Theme
+ */
+
+:root {
+ /* Core Variables (6) */
+ --nl-primary-color: #white;
+ --nl-secondary-color: #black;
+ --nl-accent-color: #ff0000;
+ --nl-muted-color: #666666;
+ --nl-font-family: "Courier New", Courier, monospace;
+ --nl-border-radius: 15px;
+ --nl-border-width: 3px;
+
+ /* Floating Tab Variables (8) */
+ --nl-tab-bg-logged-out: #ffffff;
+ --nl-tab-bg-logged-in: #000000;
+ --nl-tab-bg-opacity-logged-out: 0.9;
+ --nl-tab-bg-opacity-logged-in: 0.8;
+ --nl-tab-color-logged-out: #000000;
+ --nl-tab-color-logged-in: #ffffff;
+ --nl-tab-border-logged-out: #000000;
+ --nl-tab-border-logged-in: #ff0000;
+ --nl-tab-border-opacity-logged-out: 1.0;
+ --nl-tab-border-opacity-logged-in: 0.9;
+}
+
+/* Base component styles using simplified variables */
+.nl-component {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+.nl-button {
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ font-family: var(--nl-font-family);
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.nl-button:hover {
+ border-color: var(--nl-accent-color);
+}
+
+.nl-button:active {
+ background: var(--nl-accent-color);
+ color: var(--nl-secondary-color);
+}
+
+.nl-input {
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ font-family: var(--nl-font-family);
+ box-sizing: border-box;
+}
+
+.nl-input:focus {
+ border-color: var(--nl-accent-color);
+ outline: none;
+}
+
+.nl-container {
+ background: var(--nl-secondary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+}
+
+.nl-title, .nl-heading {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+ margin: 0;
+}
+
+.nl-text {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+.nl-text--muted {
+ color: var(--nl-muted-color);
+}
+
+.nl-icon {
+ font-family: var(--nl-font-family);
+ color: var(--nl-primary-color);
+}
+
+/* Floating tab styles */
+.nl-floating-tab {
+ font-family: var(--nl-font-family);
+ border-radius: var(--nl-border-radius);
+ border: var(--nl-border-width) solid;
+ transition: all 0.2s ease;
+}
+
+.nl-floating-tab--logged-out {
+ background: rgba(255, 255, 255, var(--nl-tab-bg-opacity-logged-out));
+ color: var(--nl-tab-color-logged-out);
+ border-color: rgba(0, 0, 0, var(--nl-tab-border-opacity-logged-out));
+}
+
+.nl-floating-tab--logged-in {
+ background: rgba(0, 0, 0, var(--nl-tab-bg-opacity-logged-in));
+ color: var(--nl-tab-color-logged-in);
+ border-color: rgba(255, 0, 0, var(--nl-tab-border-opacity-logged-in));
+}
+
+.nl-transition {
+ transition: all 0.2s ease;
+}`
+};
+
+// Theme management functions
+function injectThemeCSS(themeName = 'default') {
+ if (typeof document !== 'undefined') {
+ // Remove existing theme CSS
+ const existingStyle = document.getElementById('nl-theme-css');
+ if (existingStyle) {
+ existingStyle.remove();
+ }
+
+ // Inject selected theme CSS
+ const themeCss = THEME_CSS[themeName] || THEME_CSS['default'];
+ const style = document.createElement('style');
+ style.id = 'nl-theme-css';
+ style.textContent = themeCss;
+ document.head.appendChild(style);
+ console.log('NOSTR_LOGIN_LITE: ' + themeName + ' theme CSS injected');
+ }
+}
+
+// Auto-inject default theme when DOM is ready
+if (typeof document !== 'undefined') {
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', () => injectThemeCSS('default'));
+ } else {
+ injectThemeCSS('default');
+ }
+}
+
+// ======================================
+// Modal UI Component
+// ======================================
+
+
+class Modal {
+ constructor(options = {}) {
+ this.options = options;
+ this.container = null;
+ this.isVisible = false;
+ this.currentScreen = null;
+ this.isEmbedded = !!options.embedded;
+ this.embeddedContainer = options.embedded;
+
+ // Initialize modal container and styles
+ this._initModal();
+ }
+
+ _initModal() {
+ // Create modal container
+ this.container = document.createElement('div');
+ this.container.id = this.isEmbedded ? 'nl-modal-embedded' : 'nl-modal';
+
+ if (this.isEmbedded) {
+ // Embedded mode: inline positioning, no overlay
+ this.container.style.cssText = `
+ position: relative;
+ display: none;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ width: 100%;
+ `;
+ } else {
+ // Modal mode: fixed overlay
+ this.container.style.cssText = `
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.75);
+ display: none;
+ z-index: 10000;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+ }
+
+ // Create modal content
+ const modalContent = document.createElement('div');
+ if (this.isEmbedded) {
+ // Embedded content: no centering margin, full width
+ modalContent.style.cssText = `
+ position: relative;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ width: 100%;
+ border-radius: var(--nl-border-radius, 15px);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ overflow: hidden;
+ `;
+ } else {
+ // Modal content: centered with margin, no fixed height
+ modalContent.style.cssText = `
+ position: relative;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ width: 90%;
+ max-width: 400px;
+ margin: 50px auto;
+ border-radius: var(--nl-border-radius, 15px);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ overflow: hidden;
+ `;
+ }
+
+ // Header
+ const modalHeader = document.createElement('div');
+ modalHeader.style.cssText = `
+ padding: 20px 24px 0 24px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: transparent;
+ border-bottom: none;
+ `;
+
+ const modalTitle = document.createElement('h2');
+ modalTitle.textContent = 'Nostr Login';
+ modalTitle.style.cssText = `
+ margin: 0;
+ font-size: 24px;
+ font-weight: 600;
+ color: var(--nl-primary-color);
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ modalHeader.appendChild(modalTitle);
+
+ // Only add close button for non-embedded modals
+ // Embedded modals shouldn't have a close button because there's no way to reopen them
+ if (!this.isEmbedded) {
+ const closeButton = document.createElement('button');
+ closeButton.innerHTML = '×';
+ closeButton.onclick = () => this.close();
+ closeButton.style.cssText = `
+ background: var(--nl-secondary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: 4px;
+ font-size: 28px;
+ color: var(--nl-primary-color);
+ cursor: pointer;
+ padding: 0;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+ closeButton.onmouseover = () => {
+ closeButton.style.borderColor = 'var(--nl-accent-color)';
+ closeButton.style.background = 'var(--nl-secondary-color)';
+ };
+ closeButton.onmouseout = () => {
+ closeButton.style.borderColor = 'var(--nl-primary-color)';
+ closeButton.style.background = 'var(--nl-secondary-color)';
+ };
+
+ modalHeader.appendChild(closeButton);
+ }
+
+ // Body
+ this.modalBody = document.createElement('div');
+ this.modalBody.style.cssText = `
+ padding: 24px;
+ background: transparent;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ modalContent.appendChild(modalHeader);
+ // Add version element in bottom-right corner aligned with modal body
+ const versionElement = document.createElement('div');
+ versionElement.textContent = 'v0.1.7';
+ versionElement.style.cssText = `
+ position: absolute;
+ bottom: 8px;
+ right: 24px;
+ font-size: 14px;
+ color: #666666;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ pointer-events: none;
+ z-index: 1;
+ `;
+ modalContent.appendChild(versionElement);
+
+ modalContent.appendChild(this.modalBody);
+ this.container.appendChild(modalContent);
+
+ // Add to appropriate parent
+ if (this.isEmbedded && this.embeddedContainer) {
+ // Append to specified container for embedding
+ if (typeof this.embeddedContainer === 'string') {
+ const targetElement = document.querySelector(this.embeddedContainer);
+ if (targetElement) {
+ targetElement.appendChild(this.container);
+ } else {
+ console.error('NOSTR_LOGIN_LITE: Embedded container not found:', this.embeddedContainer);
+ document.body.appendChild(this.container);
+ }
+ } else if (this.embeddedContainer instanceof HTMLElement) {
+ this.embeddedContainer.appendChild(this.container);
+ } else {
+ console.error('NOSTR_LOGIN_LITE: Invalid embedded container');
+ document.body.appendChild(this.container);
+ }
+ } else {
+ // Add to body for modal mode
+ document.body.appendChild(this.container);
+ }
+
+ // Click outside to close (only for modal mode)
+ if (!this.isEmbedded) {
+ this.container.onclick = (e) => {
+ if (e.target === this.container) {
+ this.close();
+ }
+ };
+ }
+
+ // Update theme
+ this.updateTheme();
+ }
+
+ updateTheme() {
+ // The theme will automatically update through CSS custom properties
+ // No manual styling needed - the CSS variables handle everything
+ }
+
+ open(opts = {}) {
+ this.currentScreen = opts.startScreen;
+ this.isVisible = true;
+ this.container.style.display = 'block';
+
+ // Render login options
+ this._renderLoginOptions();
+ }
+
+ close() {
+ this.isVisible = false;
+ this.container.style.display = 'none';
+ this.modalBody.innerHTML = '';
+ }
+
+ _renderLoginOptions() {
+ this.modalBody.innerHTML = '';
+
+ const options = [];
+
+ // Extension option
+ if (this.options?.methods?.extension !== false) {
+ options.push({
+ type: 'extension',
+ title: 'Browser Extension',
+ description: 'Use your browser extension',
+ icon: '🔌'
+ });
+ }
+
+ // Local key option
+ if (this.options?.methods?.local !== false) {
+ options.push({
+ type: 'local',
+ title: 'Local Key',
+ description: 'Create or import your own key',
+ icon: '🔑'
+ });
+ }
+
+ // Seed Phrase option - only show if explicitly enabled
+ if (this.options?.methods?.seedphrase === true) {
+ options.push({
+ type: 'seedphrase',
+ title: 'Seed Phrase',
+ description: 'Import from mnemonic seed phrase',
+ icon: '🌱'
+ });
+ }
+
+ // Nostr Connect option (check both 'connect' and 'remote' for compatibility)
+ if (this.options?.methods?.connect !== false && this.options?.methods?.remote !== false) {
+ options.push({
+ type: 'connect',
+ title: 'Nostr Connect',
+ description: 'Connect with external signer',
+ icon: '🌐'
+ });
+ }
+
+ // Read-only option
+ if (this.options?.methods?.readonly !== false) {
+ options.push({
+ type: 'readonly',
+ title: 'Read Only',
+ description: 'Browse without signing',
+ icon: '👁️'
+ });
+ }
+
+ // OTP/DM option
+ if (this.options?.methods?.otp !== false) {
+ options.push({
+ type: 'otp',
+ title: 'DM/OTP',
+ description: 'Receive OTP via DM',
+ icon: '📱'
+ });
+ }
+
+ // Render each option
+ options.forEach(option => {
+ const button = document.createElement('button');
+ button.onclick = () => this._handleOptionClick(option.type);
+ button.style.cssText = `
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: 16px;
+ margin-bottom: 12px;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ cursor: pointer;
+ transition: all 0.2s;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+ button.onmouseover = () => {
+ button.style.borderColor = 'var(--nl-accent-color)';
+ button.style.background = 'var(--nl-secondary-color)';
+ };
+ button.onmouseout = () => {
+ button.style.borderColor = 'var(--nl-primary-color)';
+ button.style.background = 'var(--nl-secondary-color)';
+ };
+
+ const iconDiv = document.createElement('div');
+ // Remove the icon entirely - no emojis or text-based icons
+ iconDiv.textContent = '';
+ iconDiv.style.cssText = `
+ font-size: 16px;
+ font-weight: bold;
+ margin-right: 16px;
+ width: 0px;
+ text-align: center;
+ color: var(--nl-primary-color);
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ const contentDiv = document.createElement('div');
+ contentDiv.style.cssText = 'flex: 1; text-align: left;';
+
+ const titleDiv = document.createElement('div');
+ titleDiv.textContent = option.title;
+ titleDiv.style.cssText = `
+ font-weight: 600;
+ margin-bottom: 4px;
+ color: var(--nl-primary-color);
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ const descDiv = document.createElement('div');
+ descDiv.textContent = option.description;
+ descDiv.style.cssText = `
+ font-size: 14px;
+ color: #666666;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ contentDiv.appendChild(titleDiv);
+ contentDiv.appendChild(descDiv);
+
+ button.appendChild(iconDiv);
+ button.appendChild(contentDiv);
+ this.modalBody.appendChild(button);
+ });
+ }
+
+ _handleOptionClick(type) {
+ console.log('Selected login type:', type);
+
+ // Handle different login types
+ switch (type) {
+ case 'extension':
+ this._handleExtension();
+ break;
+ case 'local':
+ this._showLocalKeyScreen();
+ break;
+ case 'seedphrase':
+ this._showSeedPhraseScreen();
+ break;
+ case 'connect':
+ this._showConnectScreen();
+ break;
+ case 'readonly':
+ this._handleReadonly();
+ break;
+ case 'otp':
+ this._showOtpScreen();
+ break;
+ }
+ }
+
+ _handleExtension() {
+ // SIMPLIFIED ARCHITECTURE: Check for single extension at window.nostr or preserved extension
+ let extension = null;
+
+ // Check if NostrLite instance has a preserved extension (real extension detected at init)
+ if (window.NOSTR_LOGIN_LITE?._instance?.preservedExtension) {
+ extension = window.NOSTR_LOGIN_LITE._instance.preservedExtension;
+ console.log('Modal: Using preserved extension:', extension.constructor?.name);
+ }
+ // Otherwise check current window.nostr
+ else if (window.nostr && this._isRealExtension(window.nostr)) {
+ extension = window.nostr;
+ console.log('Modal: Using current window.nostr extension:', extension.constructor?.name);
+ }
+
+ if (!extension) {
+ console.log('Modal: No extension detected yet, waiting for deferred detection...');
+
+ // DEFERRED EXTENSION CHECK: Extensions like nos2x might load after our library
+ let attempts = 0;
+ const maxAttempts = 10; // Try for 2 seconds
+ const checkForExtension = () => {
+ attempts++;
+
+ // Check again for preserved extension (might be set by deferred detection)
+ if (window.NOSTR_LOGIN_LITE?._instance?.preservedExtension) {
+ extension = window.NOSTR_LOGIN_LITE._instance.preservedExtension;
+ console.log('Modal: Found preserved extension after waiting:', extension.constructor?.name);
+ this._tryExtensionLogin(extension);
+ return;
+ }
+
+ // Check current window.nostr again
+ if (window.nostr && this._isRealExtension(window.nostr)) {
+ extension = window.nostr;
+ console.log('Modal: Found extension at window.nostr after waiting:', extension.constructor?.name);
+ this._tryExtensionLogin(extension);
+ return;
+ }
+
+ // Keep trying or give up
+ if (attempts < maxAttempts) {
+ setTimeout(checkForExtension, 200);
+ } else {
+ console.log('Modal: No browser extension found after waiting 2 seconds');
+ this._showExtensionRequired();
+ }
+ };
+
+ // Start checking after a brief delay
+ setTimeout(checkForExtension, 200);
+ return;
+ }
+
+ // Use the single detected extension directly - no choice UI
+ console.log('Modal: Single extension mode - using extension directly');
+ this._tryExtensionLogin(extension);
+ }
+
+ _detectAllExtensions() {
+ const extensions = [];
+ const seenExtensions = new Set(); // Track extensions by object reference to avoid duplicates
+
+ // Extension locations to check (in priority order)
+ const locations = [
+ { path: 'window.navigator?.nostr', name: 'navigator.nostr', displayName: 'Standard Extension (navigator.nostr)', icon: '🌐', getter: () => window.navigator?.nostr },
+ { path: 'window.webln?.nostr', name: 'webln.nostr', displayName: 'Alby WebLN Extension', icon: '⚡', getter: () => window.webln?.nostr },
+ { path: 'window.alby?.nostr', name: 'alby.nostr', displayName: 'Alby Extension (Direct)', icon: '🐝', getter: () => window.alby?.nostr },
+ { path: 'window.nos2x', name: 'nos2x', displayName: 'nos2x Extension', icon: '🔌', getter: () => window.nos2x },
+ { path: 'window.flamingo?.nostr', name: 'flamingo.nostr', displayName: 'Flamingo Extension', icon: '🦩', getter: () => window.flamingo?.nostr },
+ { path: 'window.mutiny?.nostr', name: 'mutiny.nostr', displayName: 'Mutiny Extension', icon: '⚔️', getter: () => window.mutiny?.nostr },
+ { path: 'window.nostrich?.nostr', name: 'nostrich.nostr', displayName: 'Nostrich Extension', icon: '🐦', getter: () => window.nostrich?.nostr },
+ { path: 'window.getAlby?.nostr', name: 'getAlby.nostr', displayName: 'getAlby Extension', icon: '🔧', getter: () => window.getAlby?.nostr }
+ ];
+
+ // Check each location
+ for (const location of locations) {
+ try {
+ const obj = location.getter();
+
+ console.log(`Modal: Checking ${location.name}:`, !!obj, obj?.constructor?.name);
+
+ if (obj && this._isRealExtension(obj) && !seenExtensions.has(obj)) {
+ extensions.push({
+ name: location.name,
+ displayName: location.displayName,
+ icon: location.icon,
+ extension: obj
+ });
+ seenExtensions.add(obj);
+ console.log(`Modal: ✓ Detected extension at ${location.name} (${obj.constructor?.name})`);
+ } else if (obj) {
+ console.log(`Modal: ✗ Filtered out ${location.name} (${obj.constructor?.name})`);
+ }
+ } catch (e) {
+ // Location doesn't exist or can't be accessed
+ console.log(`Modal: ${location.name} not accessible:`, e.message);
+ }
+ }
+
+ // Also check window.nostr but be extra careful to avoid our library
+ console.log('Modal: Checking window.nostr:', !!window.nostr, window.nostr?.constructor?.name);
+
+ if (window.nostr) {
+ // Check if window.nostr is our WindowNostr facade with a preserved extension
+ if (window.nostr.constructor?.name === 'WindowNostr' && window.nostr.existingNostr) {
+ console.log('Modal: Found WindowNostr facade, checking existingNostr for preserved extension');
+ const preservedExtension = window.nostr.existingNostr;
+ console.log('Modal: Preserved extension:', !!preservedExtension, preservedExtension?.constructor?.name);
+
+ if (preservedExtension && this._isRealExtension(preservedExtension) && !seenExtensions.has(preservedExtension)) {
+ extensions.push({
+ name: 'window.nostr.existingNostr',
+ displayName: 'Extension (preserved by WindowNostr)',
+ icon: '🔑',
+ extension: preservedExtension
+ });
+ seenExtensions.add(preservedExtension);
+ console.log(`Modal: ✓ Detected preserved extension: ${preservedExtension.constructor?.name}`);
+ }
+ }
+ // Check if window.nostr is directly a real extension (not our facade)
+ else if (this._isRealExtension(window.nostr) && !seenExtensions.has(window.nostr)) {
+ extensions.push({
+ name: 'window.nostr',
+ displayName: 'Extension (window.nostr)',
+ icon: '🔑',
+ extension: window.nostr
+ });
+ seenExtensions.add(window.nostr);
+ console.log(`Modal: ✓ Detected extension at window.nostr: ${window.nostr.constructor?.name}`);
+ } else {
+ console.log(`Modal: ✗ Filtered out window.nostr (${window.nostr.constructor?.name}) - not a real extension`);
+ }
+ }
+
+ return extensions;
+ }
+
+ _isRealExtension(obj) {
+ console.log(`Modal: EXTENSIVE DEBUG - _isRealExtension called with:`, obj);
+ console.log(`Modal: Object type: ${typeof obj}`);
+ console.log(`Modal: Object truthy: ${!!obj}`);
+
+ if (!obj || typeof obj !== 'object') {
+ console.log(`Modal: REJECT - Not an object`);
+ return false;
+ }
+
+ console.log(`Modal: getPublicKey type: ${typeof obj.getPublicKey}`);
+ console.log(`Modal: signEvent type: ${typeof obj.signEvent}`);
+
+ // Must have required Nostr methods
+ if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
+ console.log(`Modal: REJECT - Missing required methods`);
+ return false;
+ }
+
+ // Exclude NostrTools library object
+ if (obj === window.NostrTools) {
+ console.log(`Modal: REJECT - Is NostrTools object`);
+ return false;
+ }
+
+ // Use the EXACT SAME logic as the comprehensive test (lines 804-809)
+ // This is the key fix - match the comprehensive test's successful detection logic
+ const constructorName = obj.constructor?.name;
+ const objectKeys = Object.keys(obj);
+
+ console.log(`Modal: Constructor name: "${constructorName}"`);
+ console.log(`Modal: Object keys: [${objectKeys.join(', ')}]`);
+
+ // COMPREHENSIVE TEST LOGIC - Accept anything with required methods that's not our specific library classes
+ const isRealExtension = (
+ typeof obj.getPublicKey === 'function' &&
+ typeof obj.signEvent === 'function' &&
+ constructorName !== 'WindowNostr' && // Our library class
+ constructorName !== 'NostrLite' // Our main class
+ );
+
+ console.log(`Modal: Using comprehensive test logic:`);
+ console.log(` Has getPublicKey: ${typeof obj.getPublicKey === 'function'}`);
+ console.log(` Has signEvent: ${typeof obj.signEvent === 'function'}`);
+ console.log(` Not WindowNostr: ${constructorName !== 'WindowNostr'}`);
+ console.log(` Not NostrLite: ${constructorName !== 'NostrLite'}`);
+ console.log(` Constructor: "${constructorName}"`);
+
+ // Additional debugging for comparison
+ const extensionPropChecks = {
+ _isEnabled: !!obj._isEnabled,
+ enabled: !!obj.enabled,
+ kind: !!obj.kind,
+ _eventEmitter: !!obj._eventEmitter,
+ _scope: !!obj._scope,
+ _requests: !!obj._requests,
+ _pubkey: !!obj._pubkey,
+ name: !!obj.name,
+ version: !!obj.version,
+ description: !!obj.description
+ };
+
+ console.log(`Modal: Extension property analysis:`, extensionPropChecks);
+
+ const hasExtensionProps = !!(
+ obj._isEnabled || obj.enabled || obj.kind ||
+ obj._eventEmitter || obj._scope || obj._requests || obj._pubkey ||
+ obj.name || obj.version || obj.description
+ );
+
+ const underscoreKeys = objectKeys.filter(key => key.startsWith('_'));
+ const hexToUint8Keys = objectKeys.filter(key => key.startsWith('_hex'));
+ console.log(`Modal: Underscore keys: [${underscoreKeys.join(', ')}]`);
+ console.log(`Modal: _hex* keys: [${hexToUint8Keys.join(', ')}]`);
+
+ console.log(`Modal: Additional analysis:`);
+ console.log(` hasExtensionProps: ${hasExtensionProps}`);
+ console.log(` hasLibraryMethod (_hexToUint8Array): ${objectKeys.includes('_hexToUint8Array')}`);
+
+ console.log(`Modal: COMPREHENSIVE TEST LOGIC RESULT: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
+ console.log(`Modal: FINAL DECISION for ${constructorName}: ${isRealExtension ? 'ACCEPT' : 'REJECT'}`);
+
+ return isRealExtension;
+ }
+
+ _showExtensionChoice(extensions) {
+ this.modalBody.innerHTML = '';
+
+ const title = document.createElement('h3');
+ title.textContent = 'Choose Browser Extension';
+ title.style.cssText = `
+ margin: 0 0 16px 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--nl-primary-color);
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ const description = document.createElement('p');
+ description.textContent = `Found ${extensions.length} Nostr extensions. Choose which one to use:`;
+ description.style.cssText = `
+ margin-bottom: 20px;
+ color: #666666;
+ font-size: 14px;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ this.modalBody.appendChild(title);
+ this.modalBody.appendChild(description);
+
+ // Create button for each extension
+ extensions.forEach((ext, index) => {
+ const button = document.createElement('button');
+ button.onclick = () => this._tryExtensionLogin(ext.extension);
+ button.style.cssText = `
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: 16px;
+ margin-bottom: 12px;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ cursor: pointer;
+ transition: all 0.2s;
+ text-align: left;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ button.onmouseover = () => {
+ button.style.borderColor = 'var(--nl-accent-color)';
+ button.style.background = 'var(--nl-secondary-color)';
+ };
+ button.onmouseout = () => {
+ button.style.borderColor = 'var(--nl-primary-color)';
+ button.style.background = 'var(--nl-secondary-color)';
+ };
+
+ const iconDiv = document.createElement('div');
+ iconDiv.textContent = ext.icon;
+ iconDiv.style.cssText = `
+ font-size: 24px;
+ margin-right: 16px;
+ width: 24px;
+ text-align: center;
+ `;
+
+ const contentDiv = document.createElement('div');
+ contentDiv.style.cssText = 'flex: 1;';
+
+ const nameDiv = document.createElement('div');
+ nameDiv.textContent = ext.displayName;
+ nameDiv.style.cssText = `
+ font-weight: 600;
+ margin-bottom: 4px;
+ color: var(--nl-primary-color);
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ const pathDiv = document.createElement('div');
+ pathDiv.textContent = ext.name;
+ pathDiv.style.cssText = `
+ font-size: 12px;
+ color: #666666;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ contentDiv.appendChild(nameDiv);
+ contentDiv.appendChild(pathDiv);
+
+ button.appendChild(iconDiv);
+ button.appendChild(contentDiv);
+ this.modalBody.appendChild(button);
+ });
+
+ // Add back button
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back to Login Options';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 20px;';
+
+ this.modalBody.appendChild(backButton);
+ }
+
+ async _tryExtensionLogin(extensionObj) {
+ try {
+ // Show loading state
+ this.modalBody.innerHTML = '🔄 Connecting to extension...
';
+
+ // Get pubkey from extension
+ const pubkey = await extensionObj.getPublicKey();
+ console.log('Extension provided pubkey:', pubkey);
+
+ // Set extension method with the extension object
+ this._setAuthMethod('extension', { pubkey, extension: extensionObj });
+
+ } catch (error) {
+ console.error('Extension login failed:', error);
+ this._showError(`Extension login failed: ${error.message}`);
+ }
+ }
+
+ _showLocalKeyScreen() {
+ this.modalBody.innerHTML = '';
+
+ const description = document.createElement('p');
+ description.innerHTML = 'Enter your secret key in nsec or hex format, or generate new .';
+ description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;';
+
+ const textarea = document.createElement('textarea');
+ textarea.placeholder = 'Enter your secret key:\n• nsec1... (bech32 format)\n• 64-character hex string';
+ textarea.style.cssText = `
+ width: 100%;
+ height: 100px;
+ padding: 12px;
+ border: 1px solid #d1d5db;
+ border-radius: 6px;
+ margin-bottom: 12px;
+ resize: none;
+ font-family: monospace;
+ font-size: 14px;
+ box-sizing: border-box;
+ `;
+
+ // Add real-time format detection
+ const formatHint = document.createElement('div');
+ formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;';
+
+ const importButton = document.createElement('button');
+ importButton.textContent = 'Import Key';
+ importButton.disabled = true;
+ importButton.onclick = () => {
+ if (!importButton.disabled) {
+ this._importLocalKey(textarea.value);
+ }
+ };
+
+ // Set initial disabled state
+ importButton.style.cssText = `
+ display: block;
+ width: 100%;
+ padding: 12px;
+ border: var(--nl-border-width) solid var(--nl-muted-color);
+ border-radius: var(--nl-border-radius);
+ font-size: 16px;
+ font-weight: 500;
+ cursor: not-allowed;
+ transition: all 0.2s;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ background: var(--nl-secondary-color);
+ color: var(--nl-muted-color);
+ `;
+
+ textarea.oninput = () => {
+ const value = textarea.value.trim();
+ if (!value) {
+ formatHint.textContent = '';
+ // Disable button
+ importButton.disabled = true;
+ importButton.style.borderColor = 'var(--nl-muted-color)';
+ importButton.style.color = 'var(--nl-muted-color)';
+ importButton.style.cursor = 'not-allowed';
+ return;
+ }
+
+ const format = this._detectKeyFormat(value);
+ if (format === 'nsec') {
+ formatHint.textContent = '✅ Valid nsec format detected';
+ formatHint.style.color = '#059669';
+ // Enable button
+ importButton.disabled = false;
+ importButton.style.borderColor = 'var(--nl-primary-color)';
+ importButton.style.color = 'var(--nl-primary-color)';
+ importButton.style.cursor = 'pointer';
+ } else if (format === 'hex') {
+ formatHint.textContent = '✅ Valid hex format detected';
+ formatHint.style.color = '#059669';
+ // Enable button
+ importButton.disabled = false;
+ importButton.style.borderColor = 'var(--nl-primary-color)';
+ importButton.style.color = 'var(--nl-primary-color)';
+ importButton.style.cursor = 'pointer';
+ } else {
+ formatHint.textContent = '❌ Invalid key format - must be nsec1... or 64-character hex';
+ formatHint.style.color = '#dc2626';
+ // Disable button
+ importButton.disabled = true;
+ importButton.style.borderColor = 'var(--nl-muted-color)';
+ importButton.style.color = 'var(--nl-muted-color)';
+ importButton.style.cursor = 'not-allowed';
+ }
+ };
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
+
+ this.modalBody.appendChild(description);
+ this.modalBody.appendChild(textarea);
+ this.modalBody.appendChild(formatHint);
+ this.modalBody.appendChild(importButton);
+ this.modalBody.appendChild(backButton);
+
+ // Add click handler for the "generate new" link
+ const generateLink = document.getElementById('generate-new');
+ if (generateLink) {
+ generateLink.addEventListener('mouseenter', () => {
+ generateLink.style.color = 'var(--nl-accent-color)';
+ });
+ generateLink.addEventListener('mouseleave', () => {
+ generateLink.style.color = 'var(--nl-primary-color)';
+ });
+ generateLink.addEventListener('click', () => {
+ this._generateNewLocalKey(textarea, formatHint);
+ });
+ }
+ }
+
+ _generateNewLocalKey(textarea, formatHint) {
+ try {
+ // Generate a new secret key using NostrTools
+ const sk = window.NostrTools.generateSecretKey();
+ const nsec = window.NostrTools.nip19.nsecEncode(sk);
+
+ // Set the generated key in the textarea
+ textarea.value = nsec;
+
+ // Trigger the oninput event to properly validate and enable the button
+ if (textarea.oninput) {
+ textarea.oninput();
+ }
+
+ console.log('Generated new local secret key (nsec format)');
+
+ } catch (error) {
+ console.error('Failed to generate local key:', error);
+ formatHint.textContent = '❌ Failed to generate key - NostrTools not available';
+ formatHint.style.color = '#dc2626';
+ }
+ }
+
+ _createLocalKey() {
+ try {
+ const sk = window.NostrTools.generateSecretKey();
+ const pk = window.NostrTools.getPublicKey(sk);
+ const nsec = window.NostrTools.nip19.nsecEncode(sk);
+ const npub = window.NostrTools.nip19.npubEncode(pk);
+
+ this._showKeyDisplay(pk, nsec, 'created');
+ } catch (error) {
+ this._showError('Failed to create key: ' + error.message);
+ }
+ }
+
+
+
+ _detectKeyFormat(keyValue) {
+ const trimmed = keyValue.trim();
+
+ // Check for nsec format
+ if (trimmed.startsWith('nsec1') && trimmed.length === 63) {
+ try {
+ window.NostrTools.nip19.decode(trimmed);
+ return 'nsec';
+ } catch {
+ return 'invalid';
+ }
+ }
+
+ // Check for hex format (64 characters, valid hex)
+ if (trimmed.length === 64 && /^[a-fA-F0-9]{64}$/.test(trimmed)) {
+ return 'hex';
+ }
+
+ return 'invalid';
+ }
+
+ _importLocalKey(keyValue) {
+ try {
+ const trimmed = keyValue.trim();
+ if (!trimmed) {
+ throw new Error('Please enter a secret key');
+ }
+
+ const format = this._detectKeyFormat(trimmed);
+ let sk;
+
+ if (format === 'nsec') {
+ // Decode nsec format - this returns Uint8Array
+ const decoded = window.NostrTools.nip19.decode(trimmed);
+ if (decoded.type !== 'nsec') {
+ throw new Error('Invalid nsec format');
+ }
+ sk = decoded.data; // This is already Uint8Array
+ } else if (format === 'hex') {
+ // Convert hex string to Uint8Array
+ sk = this._hexToUint8Array(trimmed);
+ // Test that it's a valid secret key by trying to get public key
+ window.NostrTools.getPublicKey(sk);
+ } else {
+ throw new Error('Invalid key format. Please enter either nsec1... or 64-character hex string');
+ }
+
+ // Generate public key and encoded formats
+ const pk = window.NostrTools.getPublicKey(sk);
+ const nsec = window.NostrTools.nip19.nsecEncode(sk);
+ const npub = window.NostrTools.nip19.npubEncode(pk);
+
+ this._showKeyDisplay(pk, nsec, 'imported');
+ } catch (error) {
+ this._showError('Invalid key: ' + error.message);
+ }
+ }
+
+ _hexToUint8Array(hex) {
+ // Convert hex string to Uint8Array
+ if (hex.length % 2 !== 0) {
+ throw new Error('Invalid hex string length');
+ }
+ const bytes = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < bytes.length; i++) {
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
+ }
+ return bytes;
+ }
+
+ _showKeyDisplay(pubkey, nsec, action) {
+ this.modalBody.innerHTML = '';
+
+ const title = document.createElement('h3');
+ title.textContent = `Key ${action} successfully!`;
+ title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600; color: #059669;';
+
+ const warningDiv = document.createElement('div');
+ warningDiv.textContent = '⚠️ Save your secret key securely!';
+ warningDiv.style.cssText = 'background: #fef3c7; color: #92400e; padding: 12px; border-radius: 6px; margin-bottom: 16px; font-size: 12px;';
+
+ // Helper function to create copy button
+ const createCopyButton = (text, label) => {
+ const copyBtn = document.createElement('button');
+ copyBtn.textContent = `Copy ${label}`;
+ copyBtn.style.cssText = `
+ margin-left: 8px;
+ padding: 4px 8px;
+ font-size: 10px;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: 1px solid var(--nl-primary-color);
+ border-radius: 4px;
+ cursor: pointer;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+ copyBtn.onclick = async (e) => {
+ e.preventDefault();
+ try {
+ await navigator.clipboard.writeText(text);
+ const originalText = copyBtn.textContent;
+ copyBtn.textContent = '✓ Copied!';
+ copyBtn.style.color = '#059669';
+ setTimeout(() => {
+ copyBtn.textContent = originalText;
+ copyBtn.style.color = 'var(--nl-primary-color)';
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy:', err);
+ copyBtn.textContent = '✗ Failed';
+ copyBtn.style.color = '#dc2626';
+ setTimeout(() => {
+ copyBtn.textContent = originalText;
+ copyBtn.style.color = 'var(--nl-primary-color)';
+ }, 2000);
+ }
+ };
+ return copyBtn;
+ };
+
+ // Convert pubkey to hex for verification
+ const pubkeyHex = typeof pubkey === 'string' ? pubkey : Array.from(pubkey).map(b => b.toString(16).padStart(2, '0')).join('');
+
+ // Decode nsec to get secret key as hex
+ let secretKeyHex = '';
+ try {
+ const decoded = window.NostrTools.nip19.decode(nsec);
+ secretKeyHex = Array.from(decoded.data).map(b => b.toString(16).padStart(2, '0')).join('');
+ } catch (err) {
+ console.error('Failed to decode nsec for hex display:', err);
+ }
+
+ // Secret Key Section
+ const nsecSection = document.createElement('div');
+ nsecSection.style.cssText = 'margin-bottom: 16px;';
+
+ const nsecLabel = document.createElement('div');
+ nsecLabel.innerHTML = 'Your Secret Key (nsec): ';
+ nsecLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
+
+ const nsecContainer = document.createElement('div');
+ nsecContainer.style.cssText = 'margin-bottom: 8px;';
+
+ const nsecCode = document.createElement('code');
+ nsecCode.textContent = nsec;
+ nsecCode.style.cssText = `
+ display: block;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ background: #f3f4f6;
+ padding: 6px;
+ border-radius: 4px;
+ font-size: 10px;
+ line-height: 1.3;
+ font-family: 'Courier New', monospace;
+ margin-bottom: 4px;
+ `;
+
+ const nsecCopyBtn = createCopyButton(nsec, 'nsec');
+ nsecCopyBtn.style.cssText += 'display: inline-block; margin-left: 0;';
+
+ nsecContainer.appendChild(nsecCode);
+ nsecContainer.appendChild(nsecCopyBtn);
+ nsecSection.appendChild(nsecLabel);
+ nsecSection.appendChild(nsecContainer);
+
+ // Secret Key Hex Section
+ if (secretKeyHex) {
+ const secretHexLabel = document.createElement('div');
+ secretHexLabel.innerHTML = 'Secret Key (hex): ';
+ secretHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
+
+ const secretHexContainer = document.createElement('div');
+ secretHexContainer.style.cssText = 'margin-bottom: 8px;';
+
+ const secretHexCode = document.createElement('code');
+ secretHexCode.textContent = secretKeyHex;
+ secretHexCode.style.cssText = `
+ display: block;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ background: #f3f4f6;
+ padding: 6px;
+ border-radius: 4px;
+ font-size: 10px;
+ line-height: 1.3;
+ font-family: 'Courier New', monospace;
+ margin-bottom: 4px;
+ `;
+
+ const secretHexCopyBtn = createCopyButton(secretKeyHex, 'hex');
+ secretHexCopyBtn.style.cssText += 'display: inline-block; margin-left: 0;';
+
+ secretHexContainer.appendChild(secretHexCode);
+ secretHexContainer.appendChild(secretHexCopyBtn);
+ nsecSection.appendChild(secretHexLabel);
+ nsecSection.appendChild(secretHexContainer);
+ }
+
+ // Public Key Section
+ const npubSection = document.createElement('div');
+ npubSection.style.cssText = 'margin-bottom: 16px;';
+
+ const npub = window.NostrTools.nip19.npubEncode(pubkey);
+
+ const npubLabel = document.createElement('div');
+ npubLabel.innerHTML = 'Your Public Key (npub): ';
+ npubLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
+
+ const npubContainer = document.createElement('div');
+ npubContainer.style.cssText = 'margin-bottom: 8px;';
+
+ const npubCode = document.createElement('code');
+ npubCode.textContent = npub;
+ npubCode.style.cssText = `
+ display: block;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ background: #f3f4f6;
+ padding: 6px;
+ border-radius: 4px;
+ font-size: 10px;
+ line-height: 1.3;
+ font-family: 'Courier New', monospace;
+ margin-bottom: 4px;
+ `;
+
+ const npubCopyBtn = createCopyButton(npub, 'npub');
+ npubCopyBtn.style.cssText += 'display: inline-block; margin-left: 0;';
+
+ npubContainer.appendChild(npubCode);
+ npubContainer.appendChild(npubCopyBtn);
+ npubSection.appendChild(npubLabel);
+ npubSection.appendChild(npubContainer);
+
+ // Public Key Hex Section
+ const pubHexLabel = document.createElement('div');
+ pubHexLabel.innerHTML = 'Public Key (hex): ';
+ pubHexLabel.style.cssText = 'margin-bottom: 4px; font-size: 12px; font-weight: 600;';
+
+ const pubHexContainer = document.createElement('div');
+ pubHexContainer.style.cssText = '';
+
+ const pubHexCode = document.createElement('code');
+ pubHexCode.textContent = pubkeyHex;
+ pubHexCode.style.cssText = `
+ display: block;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ background: #f3f4f6;
+ padding: 6px;
+ border-radius: 4px;
+ font-size: 10px;
+ line-height: 1.3;
+ font-family: 'Courier New', monospace;
+ margin-bottom: 4px;
+ `;
+
+ const pubHexCopyBtn = createCopyButton(pubkeyHex, 'hex');
+ pubHexCopyBtn.style.cssText += 'display: inline-block; margin-left: 0;';
+
+ pubHexContainer.appendChild(pubHexCode);
+ pubHexContainer.appendChild(pubHexCopyBtn);
+ npubSection.appendChild(pubHexLabel);
+ npubSection.appendChild(pubHexContainer);
+
+ const continueButton = document.createElement('button');
+ continueButton.textContent = 'Continue';
+ continueButton.onclick = () => this._setAuthMethod('local', { secret: nsec, pubkey });
+ continueButton.style.cssText = this._getButtonStyle();
+
+ this.modalBody.appendChild(title);
+ this.modalBody.appendChild(warningDiv);
+ this.modalBody.appendChild(nsecSection);
+ this.modalBody.appendChild(npubSection);
+ this.modalBody.appendChild(continueButton);
+ }
+
+ _setAuthMethod(method, options = {}) {
+ console.log('Modal: _setAuthMethod called with:', method, options);
+
+ // CRITICAL: Never install facade for extension methods - leave window.nostr as the extension
+ if (method === 'extension') {
+ console.log('Modal: Extension method - NOT installing facade, leaving window.nostr as extension');
+
+ // Save extension authentication state using global setAuthState function
+ if (typeof window.setAuthState === 'function') {
+ console.log('Modal: Saving extension auth state to storage');
+ window.setAuthState({ method, ...options }, { isolateSession: this.options?.isolateSession });
+ }
+
+ // Emit auth method selection directly for extension
+ const event = new CustomEvent('nlMethodSelected', {
+ detail: { method, ...options }
+ });
+ window.dispatchEvent(event);
+
+ this.close();
+ return;
+ }
+
+ // FOR NON-EXTENSION METHODS: Force-install facade with resilience
+ console.log('Modal: Non-extension method - FORCE-INSTALLING facade with resilience:', method);
+
+ // Store the current extension if any (for potential restoration later)
+ const currentExtension = (window.nostr?.constructor?.name !== 'WindowNostr') ? window.nostr : null;
+
+ // Get NostrLite instance for facade operations
+ const nostrLiteInstance = window.NOSTR_LOGIN_LITE?._instance;
+ if (!nostrLiteInstance || typeof nostrLiteInstance._installFacade !== 'function') {
+ console.error('Modal: Cannot access NostrLite instance or _installFacade method');
+ // Fallback: emit event anyway
+ const event = new CustomEvent('nlMethodSelected', {
+ detail: { method, ...options }
+ });
+ window.dispatchEvent(event);
+ this.close();
+ return;
+ }
+
+ // IMMEDIATE FACADE INSTALLATION
+ console.log('Modal: Installing WindowNostr facade immediately for method:', method);
+ const preservedExtension = nostrLiteInstance.preservedExtension || currentExtension;
+ nostrLiteInstance._installFacade(preservedExtension, true);
+ console.log('Modal: WindowNostr facade force-installed, current window.nostr:', window.nostr?.constructor?.name);
+
+ // DELAYED FACADE RESILIENCE - Reinstall after extension override attempts
+ const forceReinstallFacade = () => {
+ console.log('Modal: RESILIENCE CHECK - Current window.nostr after delay:', window.nostr?.constructor?.name);
+
+ // If facade was overridden by extension, reinstall it
+ if (window.nostr?.constructor?.name !== 'WindowNostr') {
+ console.log('Modal: FACADE OVERRIDDEN! Force-reinstalling WindowNostr facade for user choice:', method);
+ nostrLiteInstance._installFacade(preservedExtension, true);
+ console.log('Modal: Resilient facade force-reinstall complete, window.nostr:', window.nostr?.constructor?.name);
+
+ // Schedule another check in case of persistent extension override
+ setTimeout(() => {
+ if (window.nostr?.constructor?.name !== 'WindowNostr') {
+ console.log('Modal: PERSISTENT OVERRIDE! Final facade force-reinstall for method:', method);
+ nostrLiteInstance._installFacade(preservedExtension, true);
+ }
+ }, 1000);
+ } else {
+ console.log('Modal: Facade persistence verified - no override detected');
+ }
+ };
+
+ // Schedule resilience checks at multiple intervals
+ setTimeout(forceReinstallFacade, 100); // Quick check
+ setTimeout(forceReinstallFacade, 500); // Main check
+ setTimeout(forceReinstallFacade, 1500); // Final check
+
+ // Emit auth method selection
+ const event = new CustomEvent('nlMethodSelected', {
+ detail: { method, ...options }
+ });
+ window.dispatchEvent(event);
+
+ this.close();
+ }
+
+ _showError(message) {
+ this.modalBody.innerHTML = '';
+
+ const errorDiv = document.createElement('div');
+ errorDiv.style.cssText = 'background: #fee2e2; color: #dc2626; padding: 16px; border-radius: 6px; margin-bottom: 16px;';
+ errorDiv.innerHTML = `Error: ${message}`;
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary');
+
+ this.modalBody.appendChild(errorDiv);
+ this.modalBody.appendChild(backButton);
+ }
+
+ _showExtensionRequired() {
+ this.modalBody.innerHTML = '';
+
+ const title = document.createElement('h3');
+ title.textContent = 'Browser Extension Required';
+ title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600;';
+
+ const message = document.createElement('p');
+ message.innerHTML = `
+ Please install a Nostr browser extension and refresh the page.
+ Important: If you have multiple extensions installed, please disable all but one to avoid conflicts.
+
+ Popular extensions: Alby, nos2x, Flamingo
+ `;
+ message.style.cssText = 'margin-bottom: 20px; color: #6b7280; font-size: 14px; line-height: 1.4;';
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary');
+
+ this.modalBody.appendChild(title);
+ this.modalBody.appendChild(message);
+ this.modalBody.appendChild(backButton);
+ }
+
+ _showConnectScreen() {
+ this.modalBody.innerHTML = '';
+
+ const description = document.createElement('p');
+ description.textContent = 'Connect to a remote signer (bunker) server to use its keys for signing.';
+ description.style.cssText = 'margin-bottom: 20px; color: #6b7280; font-size: 14px;';
+
+ const formGroup = document.createElement('div');
+ formGroup.style.cssText = 'margin-bottom: 20px;';
+
+ const label = document.createElement('label');
+ label.textContent = 'Bunker Public Key:';
+ label.style.cssText = 'display: block; margin-bottom: 8px; font-weight: 500;';
+
+ const pubkeyInput = document.createElement('input');
+ pubkeyInput.type = 'text';
+ pubkeyInput.placeholder = 'bunker://pubkey?relay=..., bunker:hex, hex, or npub...';
+ pubkeyInput.style.cssText = `
+ width: 100%;
+ padding: 12px;
+ border: 1px solid #d1d5db;
+ border-radius: 6px;
+ margin-bottom: 12px;
+ font-family: monospace;
+ box-sizing: border-box;
+ `;
+
+ // Add real-time bunker key validation
+ const formatHint = document.createElement('div');
+ formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;';
+
+ const connectButton = document.createElement('button');
+ connectButton.textContent = 'Connect to Bunker';
+ connectButton.disabled = true;
+ connectButton.onclick = () => {
+ if (!connectButton.disabled) {
+ this._handleNip46Connect(pubkeyInput.value);
+ }
+ };
+
+ // Set initial disabled state
+ connectButton.style.cssText = `
+ display: block;
+ width: 100%;
+ padding: 12px;
+ border: var(--nl-border-width) solid var(--nl-muted-color);
+ border-radius: var(--nl-border-radius);
+ font-size: 16px;
+ font-weight: 500;
+ cursor: not-allowed;
+ transition: all 0.2s;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ background: var(--nl-secondary-color);
+ color: var(--nl-muted-color);
+ margin-bottom: 12px;
+ `;
+
+ pubkeyInput.oninput = () => {
+ const value = pubkeyInput.value.trim();
+ if (!value) {
+ formatHint.textContent = '';
+ // Disable button
+ connectButton.disabled = true;
+ connectButton.style.borderColor = 'var(--nl-muted-color)';
+ connectButton.style.color = 'var(--nl-muted-color)';
+ connectButton.style.cursor = 'not-allowed';
+ return;
+ }
+
+ const isValid = this._validateBunkerKey(value);
+ if (isValid) {
+ formatHint.textContent = '✅ Valid bunker connection format detected';
+ formatHint.style.color = '#059669';
+ // Enable button
+ connectButton.disabled = false;
+ connectButton.style.borderColor = 'var(--nl-primary-color)';
+ connectButton.style.color = 'var(--nl-primary-color)';
+ connectButton.style.cursor = 'pointer';
+ } else {
+ formatHint.textContent = '❌ Invalid format - must be bunker://, npub, or 64-char hex';
+ formatHint.style.color = '#dc2626';
+ // Disable button
+ connectButton.disabled = true;
+ connectButton.style.borderColor = 'var(--nl-muted-color)';
+ connectButton.style.color = 'var(--nl-muted-color)';
+ connectButton.style.cursor = 'not-allowed';
+ }
+ };
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
+
+ formGroup.appendChild(label);
+ formGroup.appendChild(pubkeyInput);
+ formGroup.appendChild(formatHint);
+
+ this.modalBody.appendChild(description);
+ this.modalBody.appendChild(formGroup);
+ this.modalBody.appendChild(connectButton);
+ this.modalBody.appendChild(backButton);
+ }
+
+ _validateBunkerKey(bunkerKey) {
+ try {
+ const trimmed = bunkerKey.trim();
+
+ // Check for bunker:// format
+ if (trimmed.startsWith('bunker://')) {
+ // Should have format: bunker://pubkey or bunker://pubkey?param=value
+ const match = trimmed.match(/^bunker:\/\/([0-9a-fA-F]{64})(\?.*)?$/);
+ return !!match;
+ }
+
+ // Check for npub format
+ if (trimmed.startsWith('npub1') && trimmed.length === 63) {
+ try {
+ if (window.NostrTools?.nip19) {
+ const decoded = window.NostrTools.nip19.decode(trimmed);
+ return decoded.type === 'npub';
+ }
+ } catch {
+ return false;
+ }
+ }
+
+ // Check for hex format (64 characters, valid hex)
+ if (trimmed.length === 64 && /^[a-fA-F0-9]{64}$/.test(trimmed)) {
+ return true;
+ }
+
+ return false;
+ } catch (error) {
+ console.log('Bunker key validation failed:', error.message);
+ return false;
+ }
+ }
+
+ _handleNip46Connect(bunkerPubkey) {
+ if (!bunkerPubkey || !bunkerPubkey.length) {
+ this._showError('Bunker pubkey is required');
+ return;
+ }
+
+ this._showNip46Connecting(bunkerPubkey);
+ this._performNip46Connect(bunkerPubkey);
+ }
+
+ _showNip46Connecting(bunkerPubkey) {
+ this.modalBody.innerHTML = '';
+
+ const title = document.createElement('h3');
+ title.textContent = 'Connecting to Remote Signer...';
+ title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600; color: #059669;';
+
+ const description = document.createElement('p');
+ description.textContent = 'Establishing secure connection to your remote signer.';
+ description.style.cssText = 'margin-bottom: 20px; color: #6b7280;';
+
+ // Normalize bunker pubkey for display (= show original format if bunker: prefix)
+ const displayPubkey = bunkerPubkey.startsWith('bunker:') || bunkerPubkey.startsWith('npub') || bunkerPubkey.length === 64 ? bunkerPubkey : bunkerPubkey;
+
+ const bunkerInfo = document.createElement('div');
+ bunkerInfo.style.cssText = 'background: #f1f5f9; padding: 12px; border-radius: 6px; margin-bottom: 20px; font-size: 14px;';
+ bunkerInfo.innerHTML = `
+ Connecting to bunker:
+ Connection: ${displayPubkey}
+ Connection string contains all necessary relay information.
+ `;
+
+ const connectingDiv = document.createElement('div');
+ connectingDiv.style.cssText = 'text-align: center; color: #6b7280;';
+ connectingDiv.innerHTML = `
+ ⏳
+ Please wait while we establish the connection...
+ This may take a few seconds
+ `;
+
+ this.modalBody.appendChild(title);
+ this.modalBody.appendChild(description);
+ this.modalBody.appendChild(bunkerInfo);
+ this.modalBody.appendChild(connectingDiv);
+ }
+
+ async _performNip46Connect(bunkerPubkey) {
+ try {
+ console.log('Starting NIP-46 connection to bunker:', bunkerPubkey);
+
+ // Check if nostr-tools NIP-46 is available
+ if (!window.NostrTools?.nip46) {
+ throw new Error('nostr-tools NIP-46 module not available');
+ }
+
+ // Use nostr-tools to parse bunker input - this handles all formats correctly
+ console.log('Parsing bunker input with nostr-tools...');
+ const bunkerPointer = await window.NostrTools.nip46.parseBunkerInput(bunkerPubkey);
+
+ if (!bunkerPointer) {
+ throw new Error('Unable to parse bunker connection string or resolve NIP-05 identifier');
+ }
+
+ console.log('Parsed bunker pointer:', bunkerPointer);
+
+ // Create local client keypair for this session
+ const localSecretKey = window.NostrTools.generateSecretKey();
+ console.log('Generated local client keypair for NIP-46 session');
+
+ // Use nostr-tools BunkerSigner factory method (not constructor - it's private)
+ console.log('Creating nip46 BunkerSigner...');
+ const signer = window.NostrTools.nip46.BunkerSigner.fromBunker(localSecretKey, bunkerPointer, {
+ onauth: (url) => {
+ console.log('Received auth URL from bunker:', url);
+ // Open auth URL in popup or redirect
+ window.open(url, '_blank', 'width=600,height=800');
+ }
+ });
+
+ console.log('NIP-46 BunkerSigner created successfully');
+
+ // Skip ping test - NIP-46 works through relays, not direct connection
+ // Try to connect directly (this may trigger auth flow)
+ console.log('Attempting NIP-46 connect...');
+ await signer.connect();
+ console.log('NIP-46 connect successful');
+
+ // Get the user's public key from the bunker
+ console.log('Getting public key from bunker...');
+ const userPubkey = await signer.getPublicKey();
+ console.log('NIP-46 user public key:', userPubkey);
+
+ // Store the NIP-46 authentication info
+ const nip46Info = {
+ pubkey: userPubkey,
+ signer: {
+ method: 'nip46',
+ remotePubkey: bunkerPointer.pubkey,
+ bunkerSigner: signer,
+ secret: bunkerPointer.secret,
+ relays: bunkerPointer.relays
+ }
+ };
+
+ console.log('NOSTR_LOGIN_LITE NIP-46 connection established successfully!');
+
+ // Set as current auth method
+ this._setAuthMethod('nip46', nip46Info);
+ return;
+
+ } catch (error) {
+ console.error('NIP-46 connection failed:', error);
+ this._showNip46Error(error.message);
+ }
+ }
+
+ _showNip46Error(message) {
+ this.modalBody.innerHTML = '';
+
+ const title = document.createElement('h3');
+ title.textContent = 'Connection Failed';
+ title.style.cssText = 'margin: 0 0 16px 0; font-size: 18px; font-weight: 600; color: #dc2626;';
+
+ const errorMsg = document.createElement('p');
+ errorMsg.textContent = `Unable to connect to remote signer: ${message}`;
+ errorMsg.style.cssText = 'margin-bottom: 20px; color: #6b7280;';
+
+ const retryButton = document.createElement('button');
+ retryButton.textContent = 'Try Again';
+ retryButton.onclick = () => this._showConnectScreen();
+ retryButton.style.cssText = this._getButtonStyle();
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back to Options';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
+
+ this.modalBody.appendChild(title);
+ this.modalBody.appendChild(errorMsg);
+ this.modalBody.appendChild(retryButton);
+ this.modalBody.appendChild(backButton);
+ }
+
+ _handleReadonly() {
+ // Set read-only mode
+ this._setAuthMethod('readonly');
+ }
+
+ _showSeedPhraseScreen() {
+ this.modalBody.innerHTML = '';
+
+ const description = document.createElement('p');
+ description.innerHTML = 'Enter your 12 or 24-word mnemonic seed phrase to derive Nostr accounts, or generate new .';
+ description.style.cssText = 'margin-bottom: 12px; color: #6b7280; font-size: 14px;';
+
+ const textarea = document.createElement('textarea');
+ // Remove default placeholder text as requested
+ textarea.placeholder = '';
+ textarea.style.cssText = `
+ width: 100%;
+ height: 100px;
+ padding: 12px;
+ border: 1px solid #d1d5db;
+ border-radius: 6px;
+ margin-bottom: 12px;
+ resize: none;
+ font-family: monospace;
+ font-size: 14px;
+ box-sizing: border-box;
+ `;
+
+ // Add real-time mnemonic validation
+ const formatHint = document.createElement('div');
+ formatHint.style.cssText = 'margin-bottom: 16px; font-size: 12px; color: #6b7280; min-height: 16px;';
+
+ const importButton = document.createElement('button');
+ importButton.textContent = 'Import Accounts';
+ importButton.disabled = true;
+ importButton.onclick = () => {
+ if (!importButton.disabled) {
+ this._importFromSeedPhrase(textarea.value);
+ }
+ };
+
+ // Set initial disabled state
+ importButton.style.cssText = `
+ display: block;
+ width: 100%;
+ padding: 12px;
+ border: var(--nl-border-width) solid var(--nl-muted-color);
+ border-radius: var(--nl-border-radius);
+ font-size: 16px;
+ font-weight: 500;
+ cursor: not-allowed;
+ transition: all 0.2s;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ background: var(--nl-secondary-color);
+ color: var(--nl-muted-color);
+ `;
+
+ textarea.oninput = () => {
+ const value = textarea.value.trim();
+ if (!value) {
+ formatHint.textContent = '';
+ // Disable button
+ importButton.disabled = true;
+ importButton.style.borderColor = 'var(--nl-muted-color)';
+ importButton.style.color = 'var(--nl-muted-color)';
+ importButton.style.cursor = 'not-allowed';
+ return;
+ }
+
+ const isValid = this._validateMnemonic(value);
+ if (isValid) {
+ const wordCount = value.split(/\s+/).length;
+ formatHint.textContent = `✅ Valid ${wordCount}-word mnemonic detected`;
+ formatHint.style.color = '#059669';
+ // Enable button
+ importButton.disabled = false;
+ importButton.style.borderColor = 'var(--nl-primary-color)';
+ importButton.style.color = 'var(--nl-primary-color)';
+ importButton.style.cursor = 'pointer';
+ } else {
+ formatHint.textContent = '❌ Invalid mnemonic - must be 12 or 24 valid BIP-39 words';
+ formatHint.style.color = '#dc2626';
+ // Disable button
+ importButton.disabled = true;
+ importButton.style.borderColor = 'var(--nl-muted-color)';
+ importButton.style.color = 'var(--nl-muted-color)';
+ importButton.style.cursor = 'not-allowed';
+ }
+ };
+
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back';
+ backButton.onclick = () => this._renderLoginOptions();
+ backButton.style.cssText = this._getButtonStyle('secondary') + 'margin-top: 12px;';
+
+ this.modalBody.appendChild(description);
+ this.modalBody.appendChild(textarea);
+ this.modalBody.appendChild(formatHint);
+ this.modalBody.appendChild(importButton);
+ this.modalBody.appendChild(backButton);
+
+ // Add click handler for the "generate new" link
+ const generateLink = document.getElementById('generate-new');
+ if (generateLink) {
+ generateLink.addEventListener('mouseenter', () => {
+ generateLink.style.color = 'var(--nl-accent-color)';
+ });
+ generateLink.addEventListener('mouseleave', () => {
+ generateLink.style.color = 'var(--nl-primary-color)';
+ });
+ generateLink.addEventListener('click', () => {
+ this._generateNewSeedPhrase(textarea, formatHint);
+ });
+ }
+ }
+
+ _generateNewSeedPhrase(textarea, formatHint) {
+ try {
+ // Check if NIP-06 is available
+ if (!window.NostrTools?.nip06) {
+ throw new Error('NIP-06 not available in bundle');
+ }
+
+ // Generate a random 12-word mnemonic using NostrTools
+ const mnemonic = window.NostrTools.nip06.generateSeedWords();
+
+ // Set the generated mnemonic in the textarea
+ textarea.value = mnemonic;
+
+ // Trigger the oninput event to properly validate and enable the button
+ if (textarea.oninput) {
+ textarea.oninput();
+ }
+
+ console.log('Generated new seed phrase:', mnemonic.split(/\s+/).length, 'words');
+
+ } catch (error) {
+ console.error('Failed to generate seed phrase:', error);
+ formatHint.textContent = '❌ Failed to generate seed phrase - NIP-06 not available';
+ formatHint.style.color = '#dc2626';
+ }
+ }
+
+ _validateMnemonic(mnemonic) {
+ try {
+ // Check if NIP-06 is available
+ if (!window.NostrTools?.nip06) {
+ console.error('NIP-06 not available in bundle');
+ return false;
+ }
+
+ const words = mnemonic.trim().split(/\s+/);
+
+ // Must be 12 or 24 words
+ if (words.length !== 12 && words.length !== 24) {
+ return false;
+ }
+
+ // Try to validate using NostrTools nip06 - this will throw if invalid
+ window.NostrTools.nip06.privateKeyFromSeedWords(mnemonic, '', 0);
+ return true;
+ } catch (error) {
+ console.log('Mnemonic validation failed:', error.message);
+ return false;
+ }
+ }
+
+ _importFromSeedPhrase(mnemonic) {
+ try {
+ const trimmed = mnemonic.trim();
+ if (!trimmed) {
+ throw new Error('Please enter a mnemonic seed phrase');
+ }
+
+ // Validate the mnemonic
+ if (!this._validateMnemonic(trimmed)) {
+ throw new Error('Invalid mnemonic. Please enter a valid 12 or 24-word BIP-39 seed phrase');
+ }
+
+ // Generate accounts 0-5 using NIP-06
+ const accounts = [];
+ for (let i = 0; i < 6; i++) {
+ try {
+ const privateKey = window.NostrTools.nip06.privateKeyFromSeedWords(trimmed, '', i);
+ const publicKey = window.NostrTools.getPublicKey(privateKey);
+ const nsec = window.NostrTools.nip19.nsecEncode(privateKey);
+ const npub = window.NostrTools.nip19.npubEncode(publicKey);
+
+ accounts.push({
+ index: i,
+ privateKey,
+ publicKey,
+ nsec,
+ npub
+ });
+ } catch (error) {
+ console.error(`Failed to derive account ${i}:`, error);
+ }
+ }
+
+ if (accounts.length === 0) {
+ throw new Error('Failed to derive any accounts from seed phrase');
+ }
+
+ console.log(`Successfully derived ${accounts.length} accounts from seed phrase`);
+ this._showAccountSelection(accounts);
+
+ } catch (error) {
+ console.error('Seed phrase import failed:', error);
+ this._showError('Seed phrase import failed: ' + error.message);
+ }
+ }
+
+ _showAccountSelection(accounts) {
+ this.modalBody.innerHTML = '';
+
+ const description = document.createElement('p');
+ description.textContent = `Select which account to use (${accounts.length} accounts derived from seed phrase):`;
+ description.style.cssText = 'margin-bottom: 20px; color: #6b7280; font-size: 14px;';
+
+ this.modalBody.appendChild(description);
+
+ // Create table for account selection
+ const table = document.createElement('table');
+ table.style.cssText = `
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 20px;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ font-size: 12px;
+ `;
+
+ // Table header
+ const thead = document.createElement('thead');
+ thead.innerHTML = `
+
+ #
+ Use
+
+ `;
+ table.appendChild(thead);
+
+ // Table body
+ const tbody = document.createElement('tbody');
+ accounts.forEach(account => {
+ const row = document.createElement('tr');
+ row.style.cssText = 'border: 1px solid #d1d5db;';
+
+ const indexCell = document.createElement('td');
+ indexCell.textContent = account.index;
+ indexCell.style.cssText = 'padding: 8px; text-align: center; border: 1px solid #d1d5db; font-weight: bold;';
+
+ const actionCell = document.createElement('td');
+ actionCell.style.cssText = 'padding: 8px; border: 1px solid #d1d5db;';
+
+ // Show truncated npub in the button
+ const truncatedNpub = `${account.npub.slice(0, 12)}...${account.npub.slice(-8)}`;
+
+ const selectButton = document.createElement('button');
+ selectButton.textContent = truncatedNpub;
+ selectButton.onclick = () => this._selectAccount(account);
+ selectButton.style.cssText = `
+ width: 100%;
+ padding: 8px 12px;
+ font-size: 11px;
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ border: 1px solid var(--nl-primary-color);
+ border-radius: 4px;
+ cursor: pointer;
+ font-family: 'Courier New', monospace;
+ text-align: center;
+ `;
+ selectButton.onmouseover = () => {
+ selectButton.style.borderColor = 'var(--nl-accent-color)';
+ };
+ selectButton.onmouseout = () => {
+ selectButton.style.borderColor = 'var(--nl-primary-color)';
+ };
+
+ actionCell.appendChild(selectButton);
+
+ row.appendChild(indexCell);
+ row.appendChild(actionCell);
+ tbody.appendChild(row);
+ });
+ table.appendChild(tbody);
+
+ this.modalBody.appendChild(table);
+
+ // Back button
+ const backButton = document.createElement('button');
+ backButton.textContent = 'Back to Seed Phrase';
+ backButton.onclick = () => this._showSeedPhraseScreen();
+ backButton.style.cssText = this._getButtonStyle('secondary');
+
+ this.modalBody.appendChild(backButton);
+ }
+
+ _selectAccount(account) {
+ console.log('Selected account:', account.index, account.npub);
+
+ // Use the same auth method as local keys, but with seedphrase identifier
+ this._setAuthMethod('local', {
+ secret: account.nsec,
+ pubkey: account.publicKey,
+ source: 'seedphrase',
+ accountIndex: account.index
+ });
+ }
+
+ _showOtpScreen() {
+ // Placeholder for OTP functionality
+ this._showError('OTP/DM not yet implemented - coming soon!');
+ }
+
+ _getButtonStyle(type = 'primary') {
+ const baseStyle = `
+ display: block;
+ width: 100%;
+ padding: 12px;
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ font-size: 16px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ font-family: var(--nl-font-family, 'Courier New', monospace);
+ `;
+
+ if (type === 'primary') {
+ return baseStyle + `
+ background: var(--nl-secondary-color);
+ color: var(--nl-primary-color);
+ `;
+ } else {
+ return baseStyle + `
+ background: #cccccc;
+ color: var(--nl-primary-color);
+ `;
+ }
+ }
+
+ // Public API
+ static init(options) {
+ if (Modal.instance) return Modal.instance;
+ Modal.instance = new Modal(options);
+ return Modal.instance;
+ }
+
+ static getInstance() {
+ return Modal.instance;
+ }
+}
+
+// Initialize global instance
+let modalInstance = null;
+
+window.addEventListener('load', () => {
+ modalInstance = new Modal();
+});
+
+
+// ======================================
+// FloatingTab Component (Recovered from git history)
+// ======================================
+
+class FloatingTab {
+ constructor(modal, options = {}) {
+ this.modal = modal;
+ this.options = {
+ enabled: true,
+ hPosition: 1.0, // 0.0 = left, 1.0 = right
+ vPosition: 0.5, // 0.0 = top, 1.0 = bottom
+ offset: { x: 0, y: 0 },
+ appearance: {
+ style: 'pill', // 'pill', 'square', 'circle'
+ theme: 'auto', // 'auto', 'light', 'dark'
+ icon: '',
+ text: 'Login',
+ iconOnly: false
+ },
+ behavior: {
+ hideWhenAuthenticated: true,
+ showUserInfo: true,
+ autoSlide: true,
+ persistent: false
+ },
+ getUserInfo: false,
+ getUserRelay: [],
+ ...options
+ };
+
+ this.userProfile = null;
+ this.container = null;
+ this.isVisible = false;
+
+ if (this.options.enabled) {
+ this._init();
+ }
+ }
+
+ _init() {
+ console.log('FloatingTab: Initializing with options:', this.options);
+ this._createContainer();
+ this._setupEventListeners();
+ this._updateAppearance();
+ this._position();
+ this.show();
+ }
+
+ // Get authentication state from authoritative source (Global Storage-Based Function)
+ _getAuthState() {
+ return window.NOSTR_LOGIN_LITE?.getAuthState?.() || null;
+ }
+
+
+ _createContainer() {
+ // Remove existing floating tab if any
+ const existingTab = document.getElementById('nl-floating-tab');
+ if (existingTab) {
+ existingTab.remove();
+ }
+
+ this.container = document.createElement('div');
+ this.container.id = 'nl-floating-tab';
+ this.container.className = 'nl-floating-tab';
+
+ // Base styles - positioning and behavior
+ this.container.style.cssText = `
+ position: fixed;
+ z-index: 9999;
+ cursor: pointer;
+ user-select: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 8px 16px;
+ min-width: 80px;
+ max-width: 200px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ `;
+
+ document.body.appendChild(this.container);
+ }
+
+ _setupEventListeners() {
+ if (!this.container) return;
+
+ // Click handler
+ this.container.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ this._handleClick();
+ });
+
+ // Hover effects
+ this.container.addEventListener('mouseenter', () => {
+ if (this.options.behavior.autoSlide) {
+ this._slideIn();
+ }
+ });
+
+ this.container.addEventListener('mouseleave', () => {
+ if (this.options.behavior.autoSlide) {
+ this._slideOut();
+ }
+ });
+
+ // Listen for authentication events
+ window.addEventListener('nlMethodSelected', (e) => {
+ console.log('🔍 FloatingTab: Authentication method selected event received');
+ console.log('🔍 FloatingTab: Event detail:', e.detail);
+ this._handleAuth(e.detail);
+ });
+
+ window.addEventListener('nlAuthRestored', (e) => {
+ console.log('🔍 FloatingTab: ✅ Authentication restored event received');
+ console.log('🔍 FloatingTab: Event detail:', e.detail);
+ console.log('🔍 FloatingTab: Calling _handleAuth with restored data...');
+ this._handleAuth(e.detail);
+ });
+
+ window.addEventListener('nlLogout', () => {
+ console.log('🔍 FloatingTab: Logout event received');
+ this._handleLogout();
+ });
+
+ // Check for existing authentication state on initialization
+ window.addEventListener('load', () => {
+ setTimeout(() => {
+ this._checkExistingAuth();
+ }, 1000); // Wait 1 second for all initialization to complete
+ });
+ }
+
+ // Check for existing authentication on page load
+ async _checkExistingAuth() {
+ console.log('🔍 FloatingTab: === _checkExistingAuth START ===');
+
+ try {
+ const storageKey = 'nostr_login_lite_auth';
+ let storedAuth = null;
+
+ // Try sessionStorage first, then localStorage
+ if (sessionStorage.getItem(storageKey)) {
+ storedAuth = JSON.parse(sessionStorage.getItem(storageKey));
+ console.log('🔍 FloatingTab: Found auth in sessionStorage:', storedAuth.method);
+ } else if (localStorage.getItem(storageKey)) {
+ storedAuth = JSON.parse(localStorage.getItem(storageKey));
+ console.log('🔍 FloatingTab: Found auth in localStorage:', storedAuth.method);
+ }
+
+ if (storedAuth) {
+ // Check if stored auth is not expired
+ const maxAge = storedAuth.method === 'extension' ? 60 * 60 * 1000 : 24 * 60 * 60 * 1000;
+ if (Date.now() - storedAuth.timestamp <= maxAge) {
+ console.log('🔍 FloatingTab: Found valid stored auth, simulating auth event');
+
+ // Create auth data object for FloatingTab
+ const authData = {
+ method: storedAuth.method,
+ pubkey: storedAuth.pubkey
+ };
+
+ // For extensions, try to find the extension
+ if (storedAuth.method === 'extension') {
+ if (window.nostr && window.nostr.constructor?.name !== 'WindowNostr') {
+ authData.extension = window.nostr;
+ }
+ }
+
+ await this._handleAuth(authData);
+ } else {
+ console.log('🔍 FloatingTab: Stored auth expired, clearing');
+ sessionStorage.removeItem(storageKey);
+ localStorage.removeItem(storageKey);
+ }
+ } else {
+ console.log('🔍 FloatingTab: No existing authentication found');
+ }
+
+ } catch (error) {
+ console.error('🔍 FloatingTab: Error checking existing auth:', error);
+ }
+
+ console.log('🔍 FloatingTab: === _checkExistingAuth END ===');
+ }
+
+ _handleClick() {
+ console.log('FloatingTab: Clicked');
+
+ const authState = this._getAuthState();
+ if (authState && this.options.behavior.showUserInfo) {
+ // Show user menu or profile options
+ this._showUserMenu();
+ } else {
+ // Always open login modal (consistent with login buttons)
+ if (this.modal) {
+ this.modal.open({ startScreen: 'login' });
+ }
+ }
+ }
+
+ // Check if object is a real extension (same logic as NostrLite._isRealExtension)
+ _isRealExtension(obj) {
+ if (!obj || typeof obj !== 'object') {
+ return false;
+ }
+
+ // Must have required Nostr methods
+ if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
+ return false;
+ }
+
+ // Exclude our own library classes
+ const constructorName = obj.constructor?.name;
+ if (constructorName === 'WindowNostr' || constructorName === 'NostrLite') {
+ return false;
+ }
+
+ // Exclude NostrTools library object
+ if (obj === window.NostrTools) {
+ return false;
+ }
+
+ // Conservative check: Look for common extension characteristics
+ const extensionIndicators = [
+ '_isEnabled', 'enabled', 'kind', '_eventEmitter', '_scope',
+ '_requests', '_pubkey', 'name', 'version', 'description'
+ ];
+
+ const hasIndicators = extensionIndicators.some(prop => obj.hasOwnProperty(prop));
+
+ // Additional check: Extensions often have specific constructor patterns
+ const hasExtensionConstructor = constructorName &&
+ constructorName !== 'Object' &&
+ constructorName !== 'Function';
+
+ return hasIndicators || hasExtensionConstructor;
+ }
+
+ // Try to login with extension and trigger proper persistence
+ async _tryExtensionLogin(extension) {
+ try {
+ console.log('FloatingTab: Attempting extension login');
+
+ // Get pubkey from extension
+ const pubkey = await extension.getPublicKey();
+ console.log('FloatingTab: Extension provided pubkey:', pubkey);
+
+ // Create extension auth data
+ const extensionAuth = {
+ method: 'extension',
+ pubkey: pubkey,
+ extension: extension
+ };
+
+ // **CRITICAL FIX**: Dispatch nlMethodSelected event to trigger persistence
+ console.log('FloatingTab: Dispatching nlMethodSelected for persistence');
+ if (typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('nlMethodSelected', {
+ detail: extensionAuth
+ }));
+ }
+
+ // Also call our local _handleAuth for UI updates
+ await this._handleAuth(extensionAuth);
+
+ } catch (error) {
+ console.error('FloatingTab: Extension login failed:', error);
+ // Fall back to opening modal
+ if (this.modal) {
+ this.modal.open({ startScreen: 'login' });
+ }
+ }
+ }
+
+ async _handleAuth(authData) {
+ console.log('🔍 FloatingTab: === _handleAuth START ===');
+ console.log('🔍 FloatingTab: authData received:', authData);
+
+ // Wait a brief moment for WindowNostr to process the authentication
+ setTimeout(async () => {
+ console.log('🔍 FloatingTab: Checking authentication state from authoritative source...');
+
+ const authState = this._getAuthState();
+ const isAuthenticated = !!authState;
+
+ console.log('🔍 FloatingTab: Authoritative auth state:', authState);
+ console.log('🔍 FloatingTab: Is authenticated:', isAuthenticated);
+
+ if (isAuthenticated) {
+ console.log('🔍 FloatingTab: ✅ Authentication verified from authoritative source');
+ } else {
+ console.error('🔍 FloatingTab: ❌ Authentication not found in authoritative source');
+ }
+
+ // Fetch user profile if enabled and we have a pubkey
+ if (this.options.getUserInfo && authData.pubkey) {
+ console.log('🔍 FloatingTab: getUserInfo enabled, fetching profile for:', authData.pubkey);
+ try {
+ const profile = await this._fetchUserProfile(authData.pubkey);
+ this.userProfile = profile;
+ console.log('🔍 FloatingTab: User profile fetched:', profile);
+ } catch (error) {
+ console.warn('🔍 FloatingTab: Failed to fetch user profile:', error);
+ this.userProfile = null;
+ }
+ } else {
+ console.log('🔍 FloatingTab: getUserInfo disabled or no pubkey, skipping profile fetch');
+ }
+
+ this._updateAppearance(); // Update UI based on authoritative state
+
+ console.log('🔍 FloatingTab: hideWhenAuthenticated option:', this.options.behavior.hideWhenAuthenticated);
+
+ if (this.options.behavior.hideWhenAuthenticated && isAuthenticated) {
+ console.log('🔍 FloatingTab: Hiding tab (hideWhenAuthenticated=true and authenticated)');
+ this.hide();
+ } else {
+ console.log('🔍 FloatingTab: Keeping tab visible');
+ }
+
+ }, 500); // Wait 500ms for WindowNostr to complete authentication processing
+
+ console.log('🔍 FloatingTab: === _handleAuth END ===');
+ }
+
+ _handleLogout() {
+ console.log('FloatingTab: Handling logout');
+ this.userProfile = null;
+
+ if (this.options.behavior.hideWhenAuthenticated) {
+ this.show();
+ }
+
+ this._updateAppearance();
+ }
+
+ _showUserMenu() {
+ // Simple user menu - could be expanded
+ const menu = document.createElement('div');
+ menu.style.cssText = `
+ position: fixed;
+ background: var(--nl-secondary-color);
+ border: var(--nl-border-width) solid var(--nl-primary-color);
+ border-radius: var(--nl-border-radius);
+ padding: 12px;
+ z-index: 10000;
+ font-family: var(--nl-font-family);
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ `;
+
+ // Position near the floating tab
+ const tabRect = this.container.getBoundingClientRect();
+ if (this.options.hPosition > 0.5) {
+ // Tab is on right side, show menu to the left
+ menu.style.right = (window.innerWidth - tabRect.left) + 'px';
+ } else {
+ // Tab is on left side, show menu to the right
+ menu.style.left = tabRect.right + 'px';
+ }
+ menu.style.top = tabRect.top + 'px';
+
+ // Menu content - use _getAuthState() as single source of truth
+ const authState = this._getAuthState();
+ let userDisplay;
+
+ if (authState?.pubkey) {
+ // Use profile name if available, otherwise pubkey
+ if (this.userProfile?.name || this.userProfile?.display_name) {
+ const userName = this.userProfile.name || this.userProfile.display_name;
+ userDisplay = userName.length > 16 ? userName.slice(0, 16) + '...' : userName;
+ } else {
+ userDisplay = authState.pubkey.slice(0, 8) + '...' + authState.pubkey.slice(-4);
+ }
+ } else {
+ userDisplay = 'Authenticated';
+ }
+
+ menu.innerHTML = `
+ ${userDisplay}
+
+ Logout
+
+ `;
+
+ document.body.appendChild(menu);
+
+ // Auto-remove menu after delay or on outside click
+ const removeMenu = () => menu.remove();
+ setTimeout(removeMenu, 5000);
+
+ document.addEventListener('click', function onOutsideClick(e) {
+ if (!menu.contains(e.target) && e.target !== this.container) {
+ removeMenu();
+ document.removeEventListener('click', onOutsideClick);
+ }
+ });
+ }
+
+ _updateAppearance() {
+ if (!this.container) return;
+
+ // Query authoritative source for all state information
+ const authState = this._getAuthState();
+ const isAuthenticated = authState !== null;
+
+ // Update content
+ if (isAuthenticated && this.options.behavior.showUserInfo) {
+ let display;
+
+ // Use profile name if available, otherwise fall back to pubkey
+ if (this.userProfile?.name || this.userProfile?.display_name) {
+ const userName = this.userProfile.name || this.userProfile.display_name;
+ display = this.options.appearance.iconOnly
+ ? userName.slice(0, 8)
+ : userName;
+ } else if (authState?.pubkey) {
+ // Fallback to pubkey display
+ display = this.options.appearance.iconOnly
+ ? authState.pubkey.slice(0, 6)
+ : authState.pubkey.slice(0, 6) + '...';
+ } else {
+ display = this.options.appearance.iconOnly ? 'User' : 'Authenticated';
+ }
+
+ this.container.textContent = display;
+ this.container.className = 'nl-floating-tab nl-floating-tab--logged-in';
+ } else {
+ const display = this.options.appearance.iconOnly ?
+ this.options.appearance.icon :
+ (this.options.appearance.icon ? this.options.appearance.icon + ' ' + this.options.appearance.text : this.options.appearance.text);
+
+ this.container.textContent = display;
+ this.container.className = 'nl-floating-tab nl-floating-tab--logged-out';
+ }
+
+ // Apply appearance styles based on current state
+ this._applyThemeStyles();
+ }
+
+ _applyThemeStyles() {
+ if (!this.container) return;
+
+ // The CSS classes will handle the theming through CSS custom properties
+ // Additional style customizations can be added here if needed
+
+ // Apply style variant
+ if (this.options.appearance.style === 'circle') {
+ this.container.style.borderRadius = '50%';
+ this.container.style.width = '48px';
+ this.container.style.height = '48px';
+ this.container.style.minWidth = '48px';
+ this.container.style.padding = '0';
+ } else if (this.options.appearance.style === 'square') {
+ this.container.style.borderRadius = '4px';
+ } else {
+ // pill style (default)
+ this.container.style.borderRadius = 'var(--nl-border-radius)';
+ }
+ }
+
+ async _fetchUserProfile(pubkey) {
+ if (!this.options.getUserInfo) {
+ console.log('FloatingTab: getUserInfo disabled, skipping profile fetch');
+ return null;
+ }
+
+ // Determine which relays to use
+ const relays = this.options.getUserRelay.length > 0
+ ? this.options.getUserRelay
+ : ['wss://relay.damus.io', 'wss://nos.lol'];
+
+ console.log('FloatingTab: Fetching profile from relays:', relays);
+
+ try {
+ // Create a SimplePool instance for querying
+ const pool = new window.NostrTools.SimplePool();
+
+ // Query for kind 0 (user metadata) events
+ const events = await pool.querySync(relays, {
+ kinds: [0],
+ authors: [pubkey],
+ limit: 1
+ }, { timeout: 5000 });
+
+ console.log('FloatingTab: Profile query returned', events.length, 'events');
+
+ if (events.length === 0) {
+ console.log('FloatingTab: No profile events found');
+ return null;
+ }
+
+ // Get the most recent event
+ const latestEvent = events.sort((a, b) => b.created_at - a.created_at)[0];
+
+ try {
+ const profile = JSON.parse(latestEvent.content);
+ console.log('FloatingTab: Parsed profile:', profile);
+
+ // Find the best name from any key containing "name" (case-insensitive)
+ let bestName = null;
+ const nameKeys = Object.keys(profile).filter(key =>
+ key.toLowerCase().includes('name') &&
+ typeof profile[key] === 'string' &&
+ profile[key].trim().length > 0
+ );
+
+ if (nameKeys.length > 0) {
+ // Find the shortest name value
+ bestName = nameKeys
+ .map(key => profile[key].trim())
+ .reduce((shortest, current) =>
+ current.length < shortest.length ? current : shortest
+ );
+ console.log('FloatingTab: Found name keys:', nameKeys, 'selected:', bestName);
+ }
+
+ // Return relevant profile fields with the best name
+ return {
+ name: bestName,
+ display_name: profile.display_name || null,
+ about: profile.about || null,
+ picture: profile.picture || null,
+ nip05: profile.nip05 || null
+ };
+ } catch (parseError) {
+ console.warn('FloatingTab: Failed to parse profile JSON:', parseError);
+ return null;
+ }
+ } catch (error) {
+ console.error('FloatingTab: Profile fetch error:', error);
+ return null;
+ }
+ }
+
+ _position() {
+ if (!this.container) return;
+
+ const padding = 16; // Distance from screen edge
+
+ // Calculate position based on percentage
+ const x = this.options.hPosition * (window.innerWidth - this.container.offsetWidth - padding * 2) + padding + this.options.offset.x;
+ const y = this.options.vPosition * (window.innerHeight - this.container.offsetHeight - padding * 2) + padding + this.options.offset.y;
+
+ this.container.style.left = x + 'px';
+ this.container.style.top = y + 'px';
+
+ console.log('FloatingTab: Positioned at (' + x + ', ' + y + ')');
+ }
+
+ _slideIn() {
+ if (!this.container || !this.options.behavior.autoSlide) return;
+
+ // Slide towards center slightly
+ const currentTransform = this.container.style.transform || '';
+ if (this.options.hPosition > 0.5) {
+ this.container.style.transform = currentTransform + ' translateX(-8px)';
+ } else {
+ this.container.style.transform = currentTransform + ' translateX(8px)';
+ }
+ }
+
+ _slideOut() {
+ if (!this.container || !this.options.behavior.autoSlide) return;
+
+ // Reset position
+ this.container.style.transform = '';
+ }
+
+ show() {
+ if (!this.container) return;
+ this.container.style.display = 'flex';
+ this.isVisible = true;
+ console.log('FloatingTab: Shown');
+ }
+
+ hide() {
+ if (!this.container) return;
+ this.container.style.display = 'none';
+ this.isVisible = false;
+ console.log('FloatingTab: Hidden');
+ }
+
+ destroy() {
+ if (this.container) {
+ this.container.remove();
+ this.container = null;
+ }
+ this.isVisible = false;
+ console.log('FloatingTab: Destroyed');
+ }
+
+ // Update options and re-apply
+ updateOptions(newOptions) {
+ this.options = { ...this.options, ...newOptions };
+ if (this.container) {
+ this._updateAppearance();
+ this._position();
+ }
+ }
+
+ // Get current state
+ getState() {
+ const authState = this._getAuthState();
+ return {
+ isVisible: this.isVisible,
+ isAuthenticated: !!authState,
+ userInfo: authState,
+ options: this.options
+ };
+ }
+}
+
+// ======================================
+// Main NOSTR_LOGIN_LITE Library
+// ======================================
+
+// Extension Bridge for managing browser extensions
+class ExtensionBridge {
+ constructor() {
+ this.extensions = new Map();
+ this.primaryExtension = null;
+ this._detectExtensions();
+ }
+
+ _detectExtensions() {
+ // Common extension locations
+ const locations = [
+ { path: 'window.nostr', name: 'Generic' },
+ { path: 'window.alby?.nostr', name: 'Alby' },
+ { path: 'window.nos2x?.nostr', name: 'nos2x' },
+ { path: 'window.flamingo?.nostr', name: 'Flamingo' },
+ { path: 'window.getAlby?.nostr', name: 'Alby Legacy' },
+ { path: 'window.mutiny?.nostr', name: 'Mutiny' }
+ ];
+
+ for (const location of locations) {
+ try {
+ const obj = eval(location.path);
+ if (obj && typeof obj.getPublicKey === 'function') {
+ this.extensions.set(location.name, {
+ name: location.name,
+ extension: obj,
+ constructor: obj.constructor?.name || 'Unknown'
+ });
+
+ if (!this.primaryExtension) {
+ this.primaryExtension = this.extensions.get(location.name);
+ }
+ }
+ } catch (e) {
+ // Extension not available
+ }
+ }
+ }
+
+ getAllExtensions() {
+ return Array.from(this.extensions.values());
+ }
+
+ getExtensionCount() {
+ return this.extensions.size;
+ }
+}
+
+// Main NostrLite class
+class NostrLite {
+ constructor() {
+ this.options = {};
+ this.extensionBridge = new ExtensionBridge();
+ this.initialized = false;
+ this.currentTheme = 'default';
+ this.modal = null;
+ this.floatingTab = null;
+ }
+
+ async init(options = {}) {
+ console.log('NOSTR_LOGIN_LITE: Initializing with options:', options);
+
+ this.options = {
+ theme: 'default',
+ persistence: true, // Enable persistent authentication by default
+ isolateSession: false, // Use localStorage by default for cross-window persistence
+ methods: {
+ extension: true,
+ local: true,
+ seedphrase: false,
+ readonly: true,
+ connect: false,
+ otp: false
+ },
+ floatingTab: {
+ enabled: false,
+ hPosition: 1.0,
+ vPosition: 0.5,
+ offset: { x: 0, y: 0 },
+ appearance: {
+ style: 'pill',
+ theme: 'auto',
+ icon: '',
+ text: 'Login',
+ iconOnly: false
+ },
+ behavior: {
+ hideWhenAuthenticated: true,
+ showUserInfo: true,
+ autoSlide: true,
+ persistent: false
+ },
+ getUserInfo: false,
+ getUserRelay: []
+ },
+ ...options
+ };
+
+ // Apply the selected theme (CSS-only)
+ this.switchTheme(this.options.theme);
+
+ // Always set up window.nostr facade to handle multiple extensions properly
+ console.log('🔍 NOSTR_LOGIN_LITE: Setting up facade before other initialization...');
+ await this._setupWindowNostrFacade();
+ console.log('🔍 NOSTR_LOGIN_LITE: Facade setup complete, continuing initialization...');
+
+ // Create modal during init (matching original git architecture)
+ this.modal = new Modal(this.options);
+ console.log('NOSTR_LOGIN_LITE: Modal created during init');
+
+ // Initialize floating tab if enabled
+ if (this.options.floatingTab.enabled) {
+ this.floatingTab = new FloatingTab(this.modal, this.options.floatingTab);
+ console.log('NOSTR_LOGIN_LITE: Floating tab initialized');
+ }
+
+ // Attempt to restore authentication state if persistence is enabled (AFTER facade is ready)
+ if (this.options.persistence) {
+ console.log('🔍 NOSTR_LOGIN_LITE: Persistence enabled, attempting auth restoration...');
+ await this._attemptAuthRestore();
+ } else {
+ console.log('🔍 NOSTR_LOGIN_LITE: Persistence disabled in options');
+ }
+
+ this.initialized = true;
+ console.log('NOSTR_LOGIN_LITE: Initialization complete');
+
+ return this;
+ }
+
+ async _setupWindowNostrFacade() {
+ if (typeof window !== 'undefined') {
+ console.log('🔍 NOSTR_LOGIN_LITE: === EXTENSION-FIRST FACADE SETUP ===');
+ console.log('🔍 NOSTR_LOGIN_LITE: Current window.nostr:', window.nostr);
+ console.log('🔍 NOSTR_LOGIN_LITE: Constructor:', window.nostr?.constructor?.name);
+
+ // EXTENSION-FIRST ARCHITECTURE: Never interfere with real extensions
+ if (this._isRealExtension(window.nostr)) {
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ REAL EXTENSION DETECTED - WILL NOT INSTALL FACADE');
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension constructor:', window.nostr.constructor?.name);
+ console.log('🔍 NOSTR_LOGIN_LITE: Extensions will handle window.nostr directly');
+
+ // Store reference for persistence verification
+ this.detectedExtension = window.nostr;
+ this.hasExtension = true;
+ this.facadeInstalled = false; // We deliberately don't install facade for extensions
+
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension mode - no facade interference');
+ return; // Don't install facade at all for extensions
+ }
+
+ // NO EXTENSION: Install facade for local/NIP-46/readonly methods
+ console.log('🔍 NOSTR_LOGIN_LITE: ❌ No real extension detected');
+ console.log('🔍 NOSTR_LOGIN_LITE: Installing facade for non-extension authentication');
+
+ this.hasExtension = false;
+ this._installFacade(window.nostr); // Install facade with any existing nostr object
+
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ Facade installed for local/NIP-46/readonly methods');
+
+ // CRITICAL FIX: Immediately attempt to restore auth state after facade installation
+ if (this.facadeInstalled && window.nostr?.restoreAuthState) {
+ console.log('🔍 NOSTR_LOGIN_LITE: 🔄 IMMEDIATELY attempting auth restoration after facade installation');
+ try {
+ const restoredAuth = await window.nostr.restoreAuthState();
+ if (restoredAuth) {
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ Auth state restored immediately during facade setup!');
+ console.log('🔍 NOSTR_LOGIN_LITE: Method:', restoredAuth.method);
+ console.log('🔍 NOSTR_LOGIN_LITE: Pubkey:', restoredAuth.pubkey);
+
+ // Update facade's authState immediately
+ window.nostr.authState = restoredAuth;
+ } else {
+ console.log('🔍 NOSTR_LOGIN_LITE: ❌ No auth state to restore during facade setup');
+ }
+ } catch (error) {
+ console.error('🔍 NOSTR_LOGIN_LITE: ❌ Error restoring auth during facade setup:', error);
+ }
+ }
+ }
+ }
+
+ _installFacade(existingNostr = null, forceInstall = false) {
+ if (typeof window !== 'undefined' && (!this.facadeInstalled || forceInstall)) {
+ console.log('🔍 NOSTR_LOGIN_LITE: === _installFacade CALLED ===');
+ console.log('🔍 NOSTR_LOGIN_LITE: existingNostr parameter:', existingNostr);
+ console.log('🔍 NOSTR_LOGIN_LITE: existingNostr constructor:', existingNostr?.constructor?.name);
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr before installation:', window.nostr);
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr constructor before:', window.nostr?.constructor?.name);
+ console.log('🔍 NOSTR_LOGIN_LITE: forceInstall flag:', forceInstall);
+
+ const facade = new WindowNostr(this, existingNostr, { isolateSession: this.options.isolateSession });
+ window.nostr = facade;
+ this.facadeInstalled = true;
+
+ console.log('🔍 NOSTR_LOGIN_LITE: === FACADE INSTALLED FOR PERSISTENCE ===');
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr after installation:', window.nostr);
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr constructor after:', window.nostr.constructor?.name);
+ console.log('🔍 NOSTR_LOGIN_LITE: facade.existingNostr:', window.nostr.existingNostr);
+ } else if (typeof window !== 'undefined') {
+ console.log('🔍 NOSTR_LOGIN_LITE: _installFacade skipped - facadeInstalled:', this.facadeInstalled, 'forceInstall:', forceInstall);
+ }
+ }
+
+ // Conservative method to identify real browser extensions
+ _isRealExtension(obj) {
+ console.log('NOSTR_LOGIN_LITE: === _isRealExtension (Conservative) ===');
+ console.log('NOSTR_LOGIN_LITE: obj:', obj);
+ console.log('NOSTR_LOGIN_LITE: typeof obj:', typeof obj);
+
+ if (!obj || typeof obj !== 'object') {
+ console.log('NOSTR_LOGIN_LITE: ✗ Not an object');
+ return false;
+ }
+
+ // Must have required Nostr methods
+ if (typeof obj.getPublicKey !== 'function' || typeof obj.signEvent !== 'function') {
+ console.log('NOSTR_LOGIN_LITE: ✗ Missing required NIP-07 methods');
+ return false;
+ }
+
+ // Exclude our own library classes
+ const constructorName = obj.constructor?.name;
+ console.log('NOSTR_LOGIN_LITE: Constructor name:', constructorName);
+
+ if (constructorName === 'WindowNostr' || constructorName === 'NostrLite') {
+ console.log('NOSTR_LOGIN_LITE: ✗ Is our library class - NOT an extension');
+ return false;
+ }
+
+ // Exclude NostrTools library object
+ if (obj === window.NostrTools) {
+ console.log('NOSTR_LOGIN_LITE: ✗ Is NostrTools object - NOT an extension');
+ return false;
+ }
+
+ // Conservative check: Look for common extension characteristics
+ // Real extensions usually have some of these internal properties
+ const extensionIndicators = [
+ '_isEnabled', 'enabled', 'kind', '_eventEmitter', '_scope',
+ '_requests', '_pubkey', 'name', 'version', 'description'
+ ];
+
+ const hasIndicators = extensionIndicators.some(prop => obj.hasOwnProperty(prop));
+
+ // Additional check: Extensions often have specific constructor patterns
+ const hasExtensionConstructor = constructorName &&
+ constructorName !== 'Object' &&
+ constructorName !== 'Function';
+
+ const isExtension = hasIndicators || hasExtensionConstructor;
+
+ console.log('NOSTR_LOGIN_LITE: Extension indicators found:', hasIndicators);
+ console.log('NOSTR_LOGIN_LITE: Has extension constructor:', hasExtensionConstructor);
+ console.log('NOSTR_LOGIN_LITE: Final result for', constructorName, ':', isExtension);
+
+ return isExtension;
+ }
+
+ launch(startScreen = 'login') {
+ console.log('NOSTR_LOGIN_LITE: Launching with screen:', startScreen);
+
+ if (this.modal) {
+ this.modal.open({ startScreen });
+ } else {
+ console.error('NOSTR_LOGIN_LITE: Modal not initialized - call init() first');
+ }
+ }
+
+ // Attempt to restore authentication state
+ async _attemptAuthRestore() {
+ try {
+ console.log('🔍 NOSTR_LOGIN_LITE: === _attemptAuthRestore START ===');
+ console.log('🔍 NOSTR_LOGIN_LITE: hasExtension:', this.hasExtension);
+ console.log('🔍 NOSTR_LOGIN_LITE: facadeInstalled:', this.facadeInstalled);
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr:', window.nostr?.constructor?.name);
+
+ if (this.hasExtension) {
+ // EXTENSION MODE: Use custom extension persistence logic
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension mode - using extension-specific restore');
+ const restoredAuth = await this._attemptExtensionRestore();
+
+ if (restoredAuth) {
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ Extension auth restored successfully!');
+ return restoredAuth;
+ } else {
+ console.log('🔍 NOSTR_LOGIN_LITE: ❌ Extension auth could not be restored');
+ return null;
+ }
+ } else if (this.facadeInstalled && window.nostr?.restoreAuthState) {
+ // NON-EXTENSION MODE: Use facade persistence logic
+ console.log('🔍 NOSTR_LOGIN_LITE: Non-extension mode - using facade restore');
+ const restoredAuth = await window.nostr.restoreAuthState();
+
+ if (restoredAuth) {
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ Facade auth restored successfully!');
+ console.log('🔍 NOSTR_LOGIN_LITE: Method:', restoredAuth.method);
+ console.log('🔍 NOSTR_LOGIN_LITE: Pubkey:', restoredAuth.pubkey);
+
+ // CRITICAL FIX: Activate facade resilience system for non-extension methods
+ // Extensions like nos2x can override our facade after page refresh
+ if (restoredAuth.method === 'local' || restoredAuth.method === 'nip46') {
+ console.log('🔍 NOSTR_LOGIN_LITE: 🛡️ Activating facade resilience system for page refresh');
+ this._activateResilienceProtection(restoredAuth.method);
+ }
+
+ // Handle NIP-46 reconnection requirement
+ if (restoredAuth.requiresReconnection) {
+ console.log('🔍 NOSTR_LOGIN_LITE: NIP-46 connection requires user reconnection');
+ this._showReconnectionPrompt(restoredAuth);
+ }
+
+ return restoredAuth;
+ } else {
+ console.log('🔍 NOSTR_LOGIN_LITE: ❌ Facade auth could not be restored');
+ return null;
+ }
+ } else {
+ console.log('🔍 NOSTR_LOGIN_LITE: ❌ No restoration method available');
+ console.log('🔍 NOSTR_LOGIN_LITE: hasExtension:', this.hasExtension);
+ console.log('🔍 NOSTR_LOGIN_LITE: facadeInstalled:', this.facadeInstalled);
+ console.log('🔍 NOSTR_LOGIN_LITE: window.nostr.restoreAuthState:', typeof window.nostr?.restoreAuthState);
+ return null;
+ }
+
+ } catch (error) {
+ console.error('🔍 NOSTR_LOGIN_LITE: Auth restoration failed with error:', error);
+ console.error('🔍 NOSTR_LOGIN_LITE: Error stack:', error.stack);
+ return null;
+ }
+ }
+
+ // Activate facade resilience protection against extension overrides
+ _activateResilienceProtection(method) {
+ console.log('🛡️ NOSTR_LOGIN_LITE: === ACTIVATING RESILIENCE PROTECTION ===');
+ console.log('🛡️ NOSTR_LOGIN_LITE: Protecting facade for method:', method);
+
+ // Store the current extension if any (for potential restoration later)
+ const preservedExtension = this.preservedExtension ||
+ ((window.nostr?.constructor?.name !== 'WindowNostr') ? window.nostr : null);
+
+ // DELAYED FACADE RESILIENCE - Reinstall after extension override attempts
+ const forceReinstallFacade = () => {
+ console.log('🛡️ NOSTR_LOGIN_LITE: RESILIENCE CHECK - Current window.nostr after delay:', window.nostr?.constructor?.name);
+
+ // If facade was overridden by extension, reinstall it
+ if (window.nostr?.constructor?.name !== 'WindowNostr') {
+ console.log('🛡️ NOSTR_LOGIN_LITE: FACADE OVERRIDDEN! Force-reinstalling WindowNostr facade for user choice:', method);
+ this._installFacade(preservedExtension, true);
+ console.log('🛡️ NOSTR_LOGIN_LITE: Resilient facade force-reinstall complete, window.nostr:', window.nostr?.constructor?.name);
+
+ // Schedule another check in case of persistent extension override
+ setTimeout(() => {
+ if (window.nostr?.constructor?.name !== 'WindowNostr') {
+ console.log('🛡️ NOSTR_LOGIN_LITE: PERSISTENT OVERRIDE! Final facade force-reinstall for method:', method);
+ this._installFacade(preservedExtension, true);
+ }
+ }, 1000);
+ } else {
+ console.log('🛡️ NOSTR_LOGIN_LITE: Facade persistence verified - no override detected');
+ }
+ };
+
+ // Schedule resilience checks at multiple intervals (same as Modal)
+ setTimeout(forceReinstallFacade, 100); // Quick check
+ setTimeout(forceReinstallFacade, 500); // Main check
+ setTimeout(forceReinstallFacade, 1500); // Final check
+
+ console.log('🛡️ NOSTR_LOGIN_LITE: Resilience protection scheduled for method:', method);
+ }
+
+ // Extension-specific authentication restoration
+ async _attemptExtensionRestore() {
+ try {
+ console.log('🔍 NOSTR_LOGIN_LITE: === _attemptExtensionRestore START ===');
+
+ // Use a simple AuthManager instance for extension persistence
+ const authManager = new AuthManager({ isolateSession: this.options?.isolateSession });
+ const storedAuth = await authManager.restoreAuthState();
+
+ if (!storedAuth || storedAuth.method !== 'extension') {
+ console.log('🔍 NOSTR_LOGIN_LITE: No extension auth state stored');
+ return null;
+ }
+
+ // Verify the extension is still available and working
+ if (!window.nostr || !this._isRealExtension(window.nostr)) {
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension no longer available');
+ authManager.clearAuthState(); // Clear invalid state
+ return null;
+ }
+
+ try {
+ // Test that the extension still works with the same pubkey
+ const currentPubkey = await window.nostr.getPublicKey();
+ if (currentPubkey !== storedAuth.pubkey) {
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension pubkey changed, clearing state');
+ authManager.clearAuthState();
+ return null;
+ }
+
+ console.log('🔍 NOSTR_LOGIN_LITE: ✅ Extension auth verification successful');
+
+ // Create extension auth data for UI restoration
+ const extensionAuth = {
+ method: 'extension',
+ pubkey: storedAuth.pubkey,
+ extension: window.nostr
+ };
+
+ // Dispatch restoration event so UI can update
+ if (typeof window !== 'undefined') {
+ console.log('🔍 NOSTR_LOGIN_LITE: Dispatching nlAuthRestored event for extension');
+ window.dispatchEvent(new CustomEvent('nlAuthRestored', {
+ detail: extensionAuth
+ }));
+ }
+
+ return extensionAuth;
+
+ } catch (error) {
+ console.log('🔍 NOSTR_LOGIN_LITE: Extension verification failed:', error);
+ authManager.clearAuthState(); // Clear invalid state
+ return null;
+ }
+
+ } catch (error) {
+ console.error('🔍 NOSTR_LOGIN_LITE: Extension restore failed:', error);
+ return null;
+ }
+ }
+
+ // Show prompt for NIP-46 reconnection
+ _showReconnectionPrompt(authData) {
+ console.log('NOSTR_LOGIN_LITE: Showing reconnection prompt for NIP-46');
+
+ // Dispatch event that UI can listen to
+ if (typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('nlReconnectionRequired', {
+ detail: {
+ method: authData.method,
+ pubkey: authData.pubkey,
+ connectionData: authData.connectionData,
+ message: 'Your NIP-46 session has expired. Please reconnect to continue.'
+ }
+ }));
+ }
+ }
+
+ logout() {
+ console.log('NOSTR_LOGIN_LITE: Logout called');
+
+ // Clear legacy stored data
+ if (typeof localStorage !== 'undefined') {
+ localStorage.removeItem('nl_current');
+ }
+
+ // Clear current authentication state directly from storage
+ // This works for ALL methods including extensions (fixes the bug)
+ clearAuthState();
+
+ // Dispatch logout event for UI updates
+ if (typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('nlLogout', {
+ detail: { timestamp: Date.now() }
+ }));
+ }
+ }
+
+ // CSS-only theme switching
+ switchTheme(themeName) {
+ console.log('NOSTR_LOGIN_LITE: Switching to ' + themeName + ' theme');
+
+ if (THEME_CSS[themeName]) {
+ injectThemeCSS(themeName);
+ this.currentTheme = themeName;
+
+ // Dispatch theme change event
+ if (typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('nlThemeChanged', {
+ detail: { theme: themeName }
+ }));
+ }
+
+ return { theme: themeName };
+ } else {
+ console.warn("Theme '" + themeName + "' not found, using default");
+ injectThemeCSS('default');
+ this.currentTheme = 'default';
+ return { theme: 'default' };
+ }
+ }
+
+ getCurrentTheme() {
+ return this.currentTheme;
+ }
+
+ getAvailableThemes() {
+ return Object.keys(THEME_CSS);
+ }
+
+ embed(container, options = {}) {
+ console.log('NOSTR_LOGIN_LITE: Creating embedded modal in container:', container);
+
+ const embedOptions = {
+ ...this.options,
+ ...options,
+ embedded: container
+ };
+
+ // Create new modal instance for embedding
+ const embeddedModal = new Modal(embedOptions);
+ embeddedModal.open();
+
+ return embeddedModal;
+ }
+
+ // Floating tab management methods
+ showFloatingTab() {
+ if (this.floatingTab) {
+ this.floatingTab.show();
+ } else {
+ console.warn('NOSTR_LOGIN_LITE: Floating tab not enabled');
+ }
+ }
+
+ hideFloatingTab() {
+ if (this.floatingTab) {
+ this.floatingTab.hide();
+ }
+ }
+
+ toggleFloatingTab() {
+ if (this.floatingTab) {
+ if (this.floatingTab.isVisible) {
+ this.floatingTab.hide();
+ } else {
+ this.floatingTab.show();
+ }
+ }
+ }
+
+ updateFloatingTab(options) {
+ if (this.floatingTab) {
+ this.floatingTab.updateOptions(options);
+ }
+ }
+
+ getFloatingTabState() {
+ return this.floatingTab ? this.floatingTab.getState() : null;
+ }
+}
+
+// ======================================
+// Simplified Authentication Manager (Unified Plaintext Storage)
+// ======================================
+
+// Simple authentication state manager - plaintext storage for maximum usability
+class AuthManager {
+ constructor(options = {}) {
+ this.storageKey = 'nostr_login_lite_auth';
+ this.currentAuthState = null;
+
+ // Configure storage type based on isolateSession option
+ if (options.isolateSession) {
+ this.storage = sessionStorage;
+ console.log('🔐 AuthManager: Using sessionStorage for per-window isolation');
+ } else {
+ this.storage = localStorage;
+ console.log('🔐 AuthManager: Using localStorage for cross-window persistence');
+ }
+
+ console.warn('🔐 SECURITY: Private keys stored unencrypted in browser storage');
+ console.warn('🔐 For production apps, implement your own secure storage');
+ }
+
+ // Save authentication state using unified plaintext approach
+ async saveAuthState(authData) {
+ try {
+ console.log('🔐 AuthManager: Saving auth state with plaintext storage');
+ console.warn('🔐 SECURITY: Private key will be stored unencrypted for maximum usability');
+
+ const authState = {
+ method: authData.method,
+ timestamp: Date.now(),
+ pubkey: authData.pubkey
+ };
+
+ switch (authData.method) {
+ case 'extension':
+ // For extensions, only store verification data - no secrets
+ authState.extensionVerification = {
+ constructor: authData.extension?.constructor?.name,
+ hasGetPublicKey: typeof authData.extension?.getPublicKey === 'function',
+ hasSignEvent: typeof authData.extension?.signEvent === 'function'
+ };
+ console.log('🔐 AuthManager: Extension method - storing verification data only');
+ break;
+
+ case 'local':
+ // UNIFIED PLAINTEXT: Store secret key directly for maximum compatibility
+ if (authData.secret) {
+ authState.secret = authData.secret;
+ console.log('🔐 AuthManager: Local method - storing secret key in plaintext');
+ console.warn('🔐 SECURITY: Secret key stored unencrypted for developer convenience');
+ }
+ break;
+
+ case 'nip46':
+ // For NIP-46, store connection parameters (no secrets)
+ if (authData.signer) {
+ authState.nip46 = {
+ remotePubkey: authData.signer.remotePubkey,
+ relays: authData.signer.relays,
+ // Don't store secret - user will need to reconnect
+ };
+ console.log('🔐 AuthManager: NIP-46 method - storing connection parameters');
+ }
+ break;
+
+ case 'readonly':
+ // Read-only mode has no secrets to store
+ console.log('🔐 AuthManager: Read-only method - storing basic auth state');
+ break;
+
+ default:
+ throw new Error('Unknown auth method: ' + authData.method);
+ }
+
+ this.storage.setItem(this.storageKey, JSON.stringify(authState));
+ this.currentAuthState = authState;
+ console.log('🔐 AuthManager: Auth state saved successfully for method:', authData.method);
+
+ } catch (error) {
+ console.error('🔐 AuthManager: Failed to save auth state:', error);
+ throw error;
+ }
+ }
+
+ // Restore authentication state on page load
+ async restoreAuthState() {
+ try {
+ console.log('🔍 AuthManager: === restoreAuthState START ===');
+ console.log('🔍 AuthManager: storageKey:', this.storageKey);
+
+ const stored = this.storage.getItem(this.storageKey);
+ console.log('🔍 AuthManager: Storage raw value:', stored);
+
+ if (!stored) {
+ console.log('🔍 AuthManager: ❌ No stored auth state found');
+ return null;
+ }
+
+ const authState = JSON.parse(stored);
+ console.log('🔍 AuthManager: ✅ Parsed stored auth state:', authState);
+ console.log('🔍 AuthManager: Method:', authState.method);
+ console.log('🔍 AuthManager: Timestamp:', authState.timestamp);
+ console.log('🔍 AuthManager: Age (ms):', Date.now() - authState.timestamp);
+
+ // Check if stored state is too old (24 hours for most methods, 1 hour for extensions)
+ const maxAge = authState.method === 'extension' ? 60 * 60 * 1000 : 24 * 60 * 60 * 1000;
+ console.log('🔍 AuthManager: Max age for method:', maxAge, 'ms');
+
+ if (Date.now() - authState.timestamp > maxAge) {
+ console.log('🔍 AuthManager: ❌ Stored auth state expired, clearing');
+ this.clearAuthState();
+ return null;
+ }
+
+ console.log('🔍 AuthManager: ✅ Auth state not expired, attempting restore for method:', authState.method);
+
+ let result;
+ switch (authState.method) {
+ case 'extension':
+ console.log('🔍 AuthManager: Calling _restoreExtensionAuth...');
+ result = await this._restoreExtensionAuth(authState);
+ break;
+
+ case 'local':
+ console.log('🔍 AuthManager: Calling _restoreLocalAuth...');
+ result = await this._restoreLocalAuth(authState);
+ break;
+
+ case 'nip46':
+ console.log('🔍 AuthManager: Calling _restoreNip46Auth...');
+ result = await this._restoreNip46Auth(authState);
+ break;
+
+ case 'readonly':
+ console.log('🔍 AuthManager: Calling _restoreReadonlyAuth...');
+ result = await this._restoreReadonlyAuth(authState);
+ break;
+
+ default:
+ console.warn('🔍 AuthManager: ❌ Unknown auth method in stored state:', authState.method);
+ return null;
+ }
+
+ console.log('🔍 AuthManager: Restore method result:', result);
+ console.log('🔍 AuthManager: === restoreAuthState END ===');
+ return result;
+
+ } catch (error) {
+ console.error('🔍 AuthManager: ❌ Failed to restore auth state:', error);
+ console.error('🔍 AuthManager: Error stack:', error.stack);
+ this.clearAuthState(); // Clear corrupted state
+ return null;
+ }
+ }
+
+ async _restoreExtensionAuth(authState) {
+ console.log('🔍 AuthManager: === _restoreExtensionAuth START ===');
+ console.log('🔍 AuthManager: authState:', authState);
+ console.log('🔍 AuthManager: window.nostr available:', !!window.nostr);
+ console.log('🔍 AuthManager: window.nostr constructor:', window.nostr?.constructor?.name);
+
+ // SMART EXTENSION WAITING SYSTEM
+ // Extensions often load after our library, so we need to wait for them
+ const extension = await this._waitForExtension(authState, 3000); // Wait up to 3 seconds
+
+ if (!extension) {
+ console.log('🔍 AuthManager: ❌ No extension found after waiting');
+ return null;
+ }
+
+ console.log('🔍 AuthManager: ✅ Extension found:', extension.constructor?.name);
+
+ try {
+ // Verify extension still works and has same pubkey
+ const currentPubkey = await extension.getPublicKey();
+ if (currentPubkey !== authState.pubkey) {
+ console.log('🔍 AuthManager: ❌ Extension pubkey changed, not restoring');
+ console.log('🔍 AuthManager: Expected:', authState.pubkey);
+ console.log('🔍 AuthManager: Got:', currentPubkey);
+ return null;
+ }
+
+ console.log('🔍 AuthManager: ✅ Extension auth restored successfully');
+ return {
+ method: 'extension',
+ pubkey: authState.pubkey,
+ extension: extension
+ };
+
+ } catch (error) {
+ console.log('🔍 AuthManager: ❌ Extension verification failed:', error);
+ return null;
+ }
+ }
+
+ // Smart extension waiting system - polls multiple locations for extensions
+ async _waitForExtension(authState, maxWaitMs = 3000) {
+ console.log('🔍 AuthManager: === _waitForExtension START ===');
+ console.log('🔍 AuthManager: maxWaitMs:', maxWaitMs);
+ console.log('🔍 AuthManager: Looking for extension with constructor:', authState.extensionVerification?.constructor);
+
+ const startTime = Date.now();
+ const pollInterval = 100; // Check every 100ms
+
+ // Extension locations to check (in priority order)
+ const extensionLocations = [
+ { path: 'window.nostr', getter: () => window.nostr },
+ { path: 'navigator.nostr', getter: () => navigator?.nostr },
+ { path: 'window.navigator?.nostr', getter: () => window.navigator?.nostr },
+ { path: 'window.alby?.nostr', getter: () => window.alby?.nostr },
+ { path: 'window.webln?.nostr', getter: () => window.webln?.nostr },
+ { path: 'window.nos2x', getter: () => window.nos2x },
+ { path: 'window.flamingo?.nostr', getter: () => window.flamingo?.nostr },
+ { path: 'window.mutiny?.nostr', getter: () => window.mutiny?.nostr }
+ ];
+
+ while (Date.now() - startTime < maxWaitMs) {
+ console.log('🔍 AuthManager: Polling for extensions... (elapsed:', Date.now() - startTime, 'ms)');
+
+ // If our facade is currently installed and blocking, temporarily remove it
+ let facadeRemoved = false;
+ let originalNostr = null;
+ if (window.nostr?.constructor?.name === 'WindowNostr') {
+ console.log('🔍 AuthManager: Temporarily removing our facade to check for real extensions');
+ originalNostr = window.nostr;
+ window.nostr = window.nostr.existingNostr || undefined;
+ facadeRemoved = true;
+ }
+
+ try {
+ // Check all extension locations
+ for (const location of extensionLocations) {
+ try {
+ const extension = location.getter();
+ console.log('🔍 AuthManager: Checking', location.path, ':', !!extension, extension?.constructor?.name);
+
+ if (this._isValidExtensionForRestore(extension, authState)) {
+ console.log('🔍 AuthManager: ✅ Found matching extension at', location.path);
+
+ // Restore facade if we removed it
+ if (facadeRemoved && originalNostr) {
+ console.log('🔍 AuthManager: Restoring facade after finding extension');
+ window.nostr = originalNostr;
+ }
+
+ return extension;
+ }
+ } catch (error) {
+ console.log('🔍 AuthManager: Error checking', location.path, ':', error.message);
+ }
+ }
+
+ // Restore facade if we removed it and haven't found an extension yet
+ if (facadeRemoved && originalNostr) {
+ window.nostr = originalNostr;
+ facadeRemoved = false;
+ }
+
+ } catch (error) {
+ console.error('🔍 AuthManager: Error during extension polling:', error);
+
+ // Restore facade if we removed it
+ if (facadeRemoved && originalNostr) {
+ window.nostr = originalNostr;
+ }
+ }
+
+ // Wait before next poll
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
+ }
+
+ console.log('🔍 AuthManager: ❌ Extension waiting timeout after', maxWaitMs, 'ms');
+ return null;
+ }
+
+ // Check if an extension is valid for restoration
+ _isValidExtensionForRestore(extension, authState) {
+ if (!extension || typeof extension !== 'object') {
+ return false;
+ }
+
+ // Must have required Nostr methods
+ if (typeof extension.getPublicKey !== 'function' ||
+ typeof extension.signEvent !== 'function') {
+ return false;
+ }
+
+ // Must not be our own classes
+ const constructorName = extension.constructor?.name;
+ if (constructorName === 'WindowNostr' || constructorName === 'NostrLite') {
+ return false;
+ }
+
+ // Must not be NostrTools
+ if (extension === window.NostrTools) {
+ return false;
+ }
+
+ // If we have stored verification data, check constructor match
+ const verification = authState.extensionVerification;
+ if (verification && verification.constructor) {
+ if (constructorName !== verification.constructor) {
+ console.log('🔍 AuthManager: Constructor mismatch -',
+ 'expected:', verification.constructor,
+ 'got:', constructorName);
+ return false;
+ }
+ }
+
+ console.log('🔍 AuthManager: ✅ Extension validation passed for:', constructorName);
+ return true;
+ }
+
+ async _restoreLocalAuth(authState) {
+ console.log('🔐 AuthManager: === _restoreLocalAuth (Unified Plaintext) ===');
+
+ // Check for legacy encrypted format first
+ if (authState.encrypted) {
+ console.log('🔐 AuthManager: Detected LEGACY encrypted format - migrating to plaintext');
+ console.warn('🔐 SECURITY: Converting from encrypted to plaintext storage for compatibility');
+
+ // Try to decrypt legacy format
+ const sessionPassword = sessionStorage.getItem('nostr_session_key');
+ if (!sessionPassword) {
+ console.log('🔐 AuthManager: Legacy session password not found - user must re-login');
+ return null;
+ }
+
+ try {
+ console.warn('🔐 AuthManager: Legacy encryption system no longer supported - user must re-login');
+ this.clearAuthState(); // Clear legacy format
+ return null;
+ } catch (error) {
+ console.error('🔐 AuthManager: Legacy decryption failed:', error);
+ this.clearAuthState(); // Clear corrupted legacy format
+ return null;
+ }
+ }
+
+ // NEW UNIFIED PLAINTEXT FORMAT
+ if (!authState.secret) {
+ console.log('🔐 AuthManager: No secret found in plaintext format');
+ return null;
+ }
+
+ console.log('🔐 AuthManager: ✅ Local auth restored from plaintext storage');
+ console.warn('🔐 SECURITY: Secret key was stored unencrypted');
+
+ return {
+ method: 'local',
+ pubkey: authState.pubkey,
+ secret: authState.secret
+ };
+ }
+
+ async _restoreNip46Auth(authState) {
+ if (!authState.nip46) {
+ console.log('🔐 AuthManager: No NIP-46 data found');
+ return null;
+ }
+
+ // For NIP-46, we can't automatically restore the connection
+ // because it requires the user to re-authenticate with the remote signer
+ // Instead, we return the connection parameters so the UI can prompt for reconnection
+ console.log('🔐 AuthManager: NIP-46 connection data found, requires user reconnection');
+ return {
+ method: 'nip46',
+ pubkey: authState.pubkey,
+ requiresReconnection: true,
+ connectionData: authState.nip46
+ };
+ }
+
+ async _restoreReadonlyAuth(authState) {
+ console.log('🔐 AuthManager: Read-only auth restored successfully');
+ return {
+ method: 'readonly',
+ pubkey: authState.pubkey
+ };
+ }
+
+ // Clear stored authentication state
+ clearAuthState() {
+ this.storage.removeItem(this.storageKey);
+ sessionStorage.removeItem('nostr_session_key'); // Clear legacy session key
+ this.currentAuthState = null;
+ console.log('🔐 AuthManager: Auth state cleared from unified storage');
+ }
+
+ // Check if we have valid stored auth
+ hasStoredAuth() {
+ const stored = this.storage.getItem(this.storageKey);
+ return !!stored;
+ }
+
+ // Get current auth method without full restoration
+ getStoredAuthMethod() {
+ try {
+ const stored = this.storage.getItem(this.storageKey);
+ if (!stored) return null;
+
+ const authState = JSON.parse(stored);
+ return authState.method;
+ } catch {
+ return null;
+ }
+ }
+}
+
+// ======================================
+// Global Authentication Functions (Single Source of Truth)
+// ======================================
+
+// Global authentication state (single source of truth)
+let globalAuthState = null;
+let globalAuthManager = null;
+
+// Initialize global auth manager (lazy initialization)
+function getGlobalAuthManager() {
+ if (!globalAuthManager) {
+ // Default to localStorage for persistence across browser sessions
+ globalAuthManager = new AuthManager({ isolateSession: false });
+ }
+ return globalAuthManager;
+}
+
+// **UNIFIED GLOBAL FUNCTION**: Set authentication state (works for all methods)
+function setAuthState(authData, options = {}) {
+ try {
+ console.log('🌐 setAuthState: Setting global auth state for method:', authData.method);
+ console.warn('🔐 SECURITY: Using unified plaintext storage for maximum compatibility');
+
+ // Store in memory
+ globalAuthState = authData;
+
+ // Store in browser storage using AuthManager
+ const authManager = new AuthManager(options);
+ authManager.saveAuthState(authData);
+
+ console.log('🌐 setAuthState: Auth state saved successfully');
+ } catch (error) {
+ console.error('🌐 setAuthState: Failed to save auth state:', error);
+ throw error;
+ }
+}
+
+// **UNIFIED GLOBAL FUNCTION**: Get authentication state (single source of truth)
+function getAuthState() {
+ try {
+ // Always query from storage as the authoritative source
+ const authManager = getGlobalAuthManager();
+ const storageKey = 'nostr_login_lite_auth';
+
+ // Check both session and local storage for compatibility
+ let stored = null;
+ if (sessionStorage.getItem(storageKey)) {
+ stored = sessionStorage.getItem(storageKey);
+ } else if (localStorage.getItem(storageKey)) {
+ stored = localStorage.getItem(storageKey);
+ }
+
+ if (!stored) {
+ console.log('🌐 getAuthState: No auth state found in storage');
+ globalAuthState = null;
+ return null;
+ }
+
+ const authState = JSON.parse(stored);
+ console.log('🌐 getAuthState: Retrieved auth state:', authState.method);
+
+ // Update in-memory cache
+ globalAuthState = authState;
+ return authState;
+
+ } catch (error) {
+ console.error('🌐 getAuthState: Failed to get auth state:', error);
+ globalAuthState = null;
+ return null;
+ }
+}
+
+// **UNIFIED GLOBAL FUNCTION**: Clear authentication state (works for all methods)
+function clearAuthState() {
+ try {
+ console.log('🌐 clearAuthState: Clearing global auth state');
+
+ // Clear in-memory state
+ globalAuthState = null;
+
+ // Clear from both storage types for thorough cleanup
+ const storageKey = 'nostr_login_lite_auth';
+ localStorage.removeItem(storageKey);
+ sessionStorage.removeItem(storageKey);
+ sessionStorage.removeItem('nostr_session_key'); // Clear legacy session key
+
+ console.log('🌐 clearAuthState: Auth state cleared from all storage locations');
+ } catch (error) {
+ console.error('🌐 clearAuthState: Failed to clear auth state:', error);
+ }
+}
+
+// NIP-07 compliant window.nostr provider
+class WindowNostr {
+ constructor(nostrLite, existingNostr = null, options = {}) {
+ this.nostrLite = nostrLite;
+ this.authState = null;
+ this.existingNostr = existingNostr;
+ this.authenticatedExtension = null;
+ this.options = options;
+ this._setupEventListeners();
+ }
+
+ // Restore authentication state on page load
+ async restoreAuthState() {
+ console.log('🔍 WindowNostr: === restoreAuthState ===');
+
+ try {
+ // Use simplified AuthManager for consistent restore logic
+ const authManager = new AuthManager(this.options);
+ const restoredAuth = await authManager.restoreAuthState();
+
+ if (restoredAuth) {
+ console.log('🔍 WindowNostr: ✅ Auth state restored:', restoredAuth.method);
+ this.authState = restoredAuth;
+
+ // Update global state
+ globalAuthState = restoredAuth;
+
+ // Dispatch restoration event
+ if (typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('nlAuthRestored', {
+ detail: restoredAuth
+ }));
+ }
+
+ return restoredAuth;
+ } else {
+ console.log('🔍 WindowNostr: ❌ No auth state to restore');
+ return null;
+ }
+
+ } catch (error) {
+ console.error('🔍 WindowNostr: Auth restoration failed:', error);
+ return null;
+ }
+ }
+
+ _setupEventListeners() {
+ // Listen for authentication events to store auth state
+ if (typeof window !== 'undefined') {
+ window.addEventListener('nlMethodSelected', async (event) => {
+ console.log('🔍 WindowNostr: nlMethodSelected event received:', event.detail);
+ this.authState = event.detail;
+
+ // If extension method, capture the specific extension the user chose
+ if (event.detail.method === 'extension') {
+ this.authenticatedExtension = event.detail.extension;
+ console.log('🔍 WindowNostr: Captured authenticated extension:', this.authenticatedExtension?.constructor?.name);
+ }
+
+ // Use unified global setAuthState function for all methods
+ try {
+ setAuthState(event.detail, this.options);
+ console.log('🔍 WindowNostr: Auth state saved via unified setAuthState');
+ } catch (error) {
+ console.error('🔍 WindowNostr: Failed to save auth state:', error);
+ }
+ });
+
+ window.addEventListener('nlLogout', () => {
+ console.log('🔍 WindowNostr: nlLogout event received');
+ this.authState = null;
+ this.authenticatedExtension = null;
+
+ // Clear from unified storage
+ clearAuthState();
+ console.log('🔍 WindowNostr: Auth state cleared via unified clearAuthState');
+ });
+ }
+ }
+
+ async getPublicKey() {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ switch (this.authState.method) {
+ case 'extension':
+ // Use the captured authenticated extension, not current window.nostr
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.getPublicKey();
+
+ case 'local':
+ case 'nip46':
+ return this.authState.pubkey;
+
+ case 'readonly':
+ throw new Error('Read-only mode - cannot get public key');
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ }
+
+ async signEvent(event) {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ if (this.authState.method === 'readonly') {
+ throw new Error('Read-only mode - cannot sign events');
+ }
+
+ switch (this.authState.method) {
+ case 'extension':
+ // Use the captured authenticated extension, not current window.nostr
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.signEvent(event);
+
+ case 'local': {
+ // Use nostr-tools to sign with local secret key
+ const { nip19, finalizeEvent } = window.NostrTools;
+ let secretKey;
+
+ if (this.authState.secret.startsWith('nsec')) {
+ const decoded = nip19.decode(this.authState.secret);
+ secretKey = decoded.data;
+ } else {
+ // Convert hex to Uint8Array
+ secretKey = this._hexToUint8Array(this.authState.secret);
+ }
+
+ return finalizeEvent(event, secretKey);
+ }
+
+ case 'nip46': {
+ // Use BunkerSigner for NIP-46
+ if (!this.authState.signer?.bunkerSigner) {
+ throw new Error('NIP-46 signer not available');
+ }
+ return await this.authState.signer.bunkerSigner.signEvent(event);
+ }
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ }
+
+ async getRelays() {
+ // Return configured relays from nostr-lite options
+ return this.nostrLite.options?.relays || ['wss://relay.damus.io'];
+ }
+
+ get nip04() {
+ return {
+ encrypt: async (pubkey, plaintext) => {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ if (this.authState.method === 'readonly') {
+ throw new Error('Read-only mode - cannot encrypt');
+ }
+
+ switch (this.authState.method) {
+ case 'extension': {
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.nip04.encrypt(pubkey, plaintext);
+ }
+
+ case 'local': {
+ const { nip04, nip19 } = window.NostrTools;
+ let secretKey;
+
+ if (this.authState.secret.startsWith('nsec')) {
+ const decoded = nip19.decode(this.authState.secret);
+ secretKey = decoded.data;
+ } else {
+ secretKey = this._hexToUint8Array(this.authState.secret);
+ }
+
+ return await nip04.encrypt(secretKey, pubkey, plaintext);
+ }
+
+ case 'nip46': {
+ if (!this.authState.signer?.bunkerSigner) {
+ throw new Error('NIP-46 signer not available');
+ }
+ return await this.authState.signer.bunkerSigner.nip04Encrypt(pubkey, plaintext);
+ }
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ },
+
+ decrypt: async (pubkey, ciphertext) => {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ if (this.authState.method === 'readonly') {
+ throw new Error('Read-only mode - cannot decrypt');
+ }
+
+ switch (this.authState.method) {
+ case 'extension': {
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.nip04.decrypt(pubkey, ciphertext);
+ }
+
+ case 'local': {
+ const { nip04, nip19 } = window.NostrTools;
+ let secretKey;
+
+ if (this.authState.secret.startsWith('nsec')) {
+ const decoded = nip19.decode(this.authState.secret);
+ secretKey = decoded.data;
+ } else {
+ secretKey = this._hexToUint8Array(this.authState.secret);
+ }
+
+ return await nip04.decrypt(secretKey, pubkey, ciphertext);
+ }
+
+ case 'nip46': {
+ if (!this.authState.signer?.bunkerSigner) {
+ throw new Error('NIP-46 signer not available');
+ }
+ return await this.authState.signer.bunkerSigner.nip04Decrypt(pubkey, ciphertext);
+ }
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ }
+ };
+ }
+
+ get nip44() {
+ return {
+ encrypt: async (pubkey, plaintext) => {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ if (this.authState.method === 'readonly') {
+ throw new Error('Read-only mode - cannot encrypt');
+ }
+
+ switch (this.authState.method) {
+ case 'extension': {
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.nip44.encrypt(pubkey, plaintext);
+ }
+
+ case 'local': {
+ const { nip44, nip19 } = window.NostrTools;
+ let secretKey;
+
+ if (this.authState.secret.startsWith('nsec')) {
+ const decoded = nip19.decode(this.authState.secret);
+ secretKey = decoded.data;
+ } else {
+ secretKey = this._hexToUint8Array(this.authState.secret);
+ }
+
+ return nip44.encrypt(plaintext, nip44.getConversationKey(secretKey, pubkey));
+ }
+
+ case 'nip46': {
+ if (!this.authState.signer?.bunkerSigner) {
+ throw new Error('NIP-46 signer not available');
+ }
+ return await this.authState.signer.bunkerSigner.nip44Encrypt(pubkey, plaintext);
+ }
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ },
+
+ decrypt: async (pubkey, ciphertext) => {
+ if (!this.authState) {
+ throw new Error('Not authenticated - use NOSTR_LOGIN_LITE.launch()');
+ }
+
+ if (this.authState.method === 'readonly') {
+ throw new Error('Read-only mode - cannot decrypt');
+ }
+
+ switch (this.authState.method) {
+ case 'extension': {
+ const ext = this.authenticatedExtension || this.authState.extension || this.existingNostr;
+ if (!ext) throw new Error('Extension not available');
+ return await ext.nip44.decrypt(pubkey, ciphertext);
+ }
+
+ case 'local': {
+ const { nip44, nip19 } = window.NostrTools;
+ let secretKey;
+
+ if (this.authState.secret.startsWith('nsec')) {
+ const decoded = nip19.decode(this.authState.secret);
+ secretKey = decoded.data;
+ } else {
+ secretKey = this._hexToUint8Array(this.authState.secret);
+ }
+
+ return nip44.decrypt(ciphertext, nip44.getConversationKey(secretKey, pubkey));
+ }
+
+ case 'nip46': {
+ if (!this.authState.signer?.bunkerSigner) {
+ throw new Error('NIP-46 signer not available');
+ }
+ return await this.authState.signer.bunkerSigner.nip44Decrypt(pubkey, ciphertext);
+ }
+
+ default:
+ throw new Error('Unsupported auth method: ' + this.authState.method);
+ }
+ }
+ };
+ }
+
+ _hexToUint8Array(hex) {
+ if (hex.length % 2 !== 0) {
+ throw new Error('Invalid hex string length');
+ }
+ const bytes = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < bytes.length; i++) {
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
+ }
+ return bytes;
+ }
+}
+
+// Initialize and export
+if (typeof window !== 'undefined') {
+ const nostrLite = new NostrLite();
+
+ // Export main API
+ window.NOSTR_LOGIN_LITE = {
+ init: (options) => nostrLite.init(options),
+ launch: (startScreen) => nostrLite.launch(startScreen),
+ logout: () => nostrLite.logout(),
+
+ // Embedded modal method
+ embed: (container, options) => nostrLite.embed(container, options),
+
+ // CSS-only theme management API
+ switchTheme: (themeName) => nostrLite.switchTheme(themeName),
+ getCurrentTheme: () => nostrLite.getCurrentTheme(),
+ getAvailableThemes: () => nostrLite.getAvailableThemes(),
+
+ // Floating tab management API
+ showFloatingTab: () => nostrLite.showFloatingTab(),
+ hideFloatingTab: () => nostrLite.hideFloatingTab(),
+ toggleFloatingTab: () => nostrLite.toggleFloatingTab(),
+ updateFloatingTab: (options) => nostrLite.updateFloatingTab(options),
+ getFloatingTabState: () => nostrLite.getFloatingTabState(),
+
+ // Global authentication state management (single source of truth)
+ setAuthState: setAuthState,
+ getAuthState: getAuthState,
+ clearAuthState: clearAuthState,
+
+ // Expose for debugging
+ _extensionBridge: nostrLite.extensionBridge,
+ _instance: nostrLite
+ };
+
+ console.log('NOSTR_LOGIN_LITE: Library loaded and ready');
+ console.log('NOSTR_LOGIN_LITE: Use window.NOSTR_LOGIN_LITE.init(options) to initialize');
+ console.log('NOSTR_LOGIN_LITE: Detected', nostrLite.extensionBridge.getExtensionCount(), 'browser extensions');
+ console.warn('🔐 SECURITY: Unified plaintext storage enabled for maximum developer usability');
+} else {
+ // Node.js environment
+ module.exports = { NostrLite };
+}
+
diff --git a/admin/assets/nostr.bundle.js b/admin/assets/nostr.bundle.js
new file mode 100644
index 0000000..5d106e4
--- /dev/null
+++ b/admin/assets/nostr.bundle.js
@@ -0,0 +1,11628 @@
+"use strict";
+var NostrTools = (() => {
+ var __defProp = Object.defineProperty;
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+ var __getOwnPropNames = Object.getOwnPropertyNames;
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
+ var __export = (target, all) => {
+ for (var name in all)
+ __defProp(target, name, { get: all[name], enumerable: true });
+ };
+ var __copyProps = (to, from, except, desc) => {
+ if (from && typeof from === "object" || typeof from === "function") {
+ for (let key of __getOwnPropNames(from))
+ if (!__hasOwnProp.call(to, key) && key !== except)
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+ }
+ return to;
+ };
+ var __toCommonJS = (mod3) => __copyProps(__defProp({}, "__esModule", { value: true }), mod3);
+
+ // index.ts
+ var nostr_tools_exports = {};
+ __export(nostr_tools_exports, {
+ Relay: () => Relay,
+ SimplePool: () => SimplePool,
+ finalizeEvent: () => finalizeEvent,
+ fj: () => fakejson_exports,
+ generateSecretKey: () => generateSecretKey,
+ getEventHash: () => getEventHash,
+ getFilterLimit: () => getFilterLimit,
+ getPublicKey: () => getPublicKey,
+ kinds: () => kinds_exports,
+ matchFilter: () => matchFilter,
+ matchFilters: () => matchFilters,
+ mergeFilters: () => mergeFilters,
+ nip04: () => nip04_exports,
+ nip05: () => nip05_exports,
+ nip06: () => nip06_exports,
+ nip10: () => nip10_exports,
+ nip11: () => nip11_exports,
+ nip13: () => nip13_exports,
+ nip17: () => nip17_exports,
+ nip18: () => nip18_exports,
+ nip19: () => nip19_exports,
+ nip21: () => nip21_exports,
+ nip25: () => nip25_exports,
+ nip27: () => nip27_exports,
+ nip28: () => nip28_exports,
+ nip30: () => nip30_exports,
+ nip39: () => nip39_exports,
+ nip42: () => nip42_exports,
+ nip44: () => nip44_exports,
+ nip46: () => nip46_exports,
+ nip47: () => nip47_exports,
+ nip54: () => nip54_exports,
+ nip57: () => nip57_exports,
+ nip59: () => nip59_exports,
+ nip98: () => nip98_exports,
+ parseReferences: () => parseReferences,
+ serializeEvent: () => serializeEvent,
+ sortEvents: () => sortEvents,
+ utils: () => utils_exports2,
+ validateEvent: () => validateEvent,
+ verifiedSymbol: () => verifiedSymbol,
+ verifyEvent: () => verifyEvent
+ });
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/_assert.js
+ function number(n) {
+ if (!Number.isSafeInteger(n) || n < 0)
+ throw new Error(`Wrong positive integer: ${n}`);
+ }
+ function bytes(b, ...lengths) {
+ if (!(b instanceof Uint8Array))
+ throw new Error("Expected Uint8Array");
+ if (lengths.length > 0 && !lengths.includes(b.length))
+ throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
+ }
+ function hash(hash3) {
+ if (typeof hash3 !== "function" || typeof hash3.create !== "function")
+ throw new Error("Hash should be wrapped by utils.wrapConstructor");
+ number(hash3.outputLen);
+ number(hash3.blockLen);
+ }
+ function exists(instance, checkFinished = true) {
+ if (instance.destroyed)
+ throw new Error("Hash instance has been destroyed");
+ if (checkFinished && instance.finished)
+ throw new Error("Hash#digest() has already been called");
+ }
+ function output(out, instance) {
+ bytes(out);
+ const min = instance.outputLen;
+ if (out.length < min) {
+ throw new Error(`digestInto() expects output buffer of length at least ${min}`);
+ }
+ }
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/crypto.js
+ var crypto = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0;
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/utils.js
+ var u8a = (a) => a instanceof Uint8Array;
+ var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
+ var rotr = (word, shift) => word << 32 - shift | word >>> shift;
+ var isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
+ if (!isLE)
+ throw new Error("Non little-endian hardware is not supported");
+ function utf8ToBytes(str) {
+ if (typeof str !== "string")
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
+ return new Uint8Array(new TextEncoder().encode(str));
+ }
+ function toBytes(data) {
+ if (typeof data === "string")
+ data = utf8ToBytes(data);
+ if (!u8a(data))
+ throw new Error(`expected Uint8Array, got ${typeof data}`);
+ return data;
+ }
+ function concatBytes(...arrays) {
+ const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
+ let pad2 = 0;
+ arrays.forEach((a) => {
+ if (!u8a(a))
+ throw new Error("Uint8Array expected");
+ r.set(a, pad2);
+ pad2 += a.length;
+ });
+ return r;
+ }
+ var Hash = class {
+ clone() {
+ return this._cloneInto();
+ }
+ };
+ var toStr = {}.toString;
+ function wrapConstructor(hashCons) {
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
+ const tmp = hashCons();
+ hashC.outputLen = tmp.outputLen;
+ hashC.blockLen = tmp.blockLen;
+ hashC.create = () => hashCons();
+ return hashC;
+ }
+ function randomBytes(bytesLength = 32) {
+ if (crypto && typeof crypto.getRandomValues === "function") {
+ return crypto.getRandomValues(new Uint8Array(bytesLength));
+ }
+ throw new Error("crypto.getRandomValues must be defined");
+ }
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/_sha2.js
+ function setBigUint64(view, byteOffset, value, isLE4) {
+ if (typeof view.setBigUint64 === "function")
+ return view.setBigUint64(byteOffset, value, isLE4);
+ const _32n2 = BigInt(32);
+ const _u32_max = BigInt(4294967295);
+ const wh = Number(value >> _32n2 & _u32_max);
+ const wl = Number(value & _u32_max);
+ const h = isLE4 ? 4 : 0;
+ const l = isLE4 ? 0 : 4;
+ view.setUint32(byteOffset + h, wh, isLE4);
+ view.setUint32(byteOffset + l, wl, isLE4);
+ }
+ var SHA2 = class extends Hash {
+ constructor(blockLen, outputLen, padOffset, isLE4) {
+ super();
+ this.blockLen = blockLen;
+ this.outputLen = outputLen;
+ this.padOffset = padOffset;
+ this.isLE = isLE4;
+ this.finished = false;
+ this.length = 0;
+ this.pos = 0;
+ this.destroyed = false;
+ this.buffer = new Uint8Array(blockLen);
+ this.view = createView(this.buffer);
+ }
+ update(data) {
+ exists(this);
+ const { view, buffer, blockLen } = this;
+ data = toBytes(data);
+ const len = data.length;
+ for (let pos = 0; pos < len; ) {
+ const take = Math.min(blockLen - this.pos, len - pos);
+ if (take === blockLen) {
+ const dataView = createView(data);
+ for (; blockLen <= len - pos; pos += blockLen)
+ this.process(dataView, pos);
+ continue;
+ }
+ buffer.set(data.subarray(pos, pos + take), this.pos);
+ this.pos += take;
+ pos += take;
+ if (this.pos === blockLen) {
+ this.process(view, 0);
+ this.pos = 0;
+ }
+ }
+ this.length += data.length;
+ this.roundClean();
+ return this;
+ }
+ digestInto(out) {
+ exists(this);
+ output(out, this);
+ this.finished = true;
+ const { buffer, view, blockLen, isLE: isLE4 } = this;
+ let { pos } = this;
+ buffer[pos++] = 128;
+ this.buffer.subarray(pos).fill(0);
+ if (this.padOffset > blockLen - pos) {
+ this.process(view, 0);
+ pos = 0;
+ }
+ for (let i2 = pos; i2 < blockLen; i2++)
+ buffer[i2] = 0;
+ setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE4);
+ this.process(view, 0);
+ const oview = createView(out);
+ const len = this.outputLen;
+ if (len % 4)
+ throw new Error("_sha2: outputLen should be aligned to 32bit");
+ const outLen = len / 4;
+ const state = this.get();
+ if (outLen > state.length)
+ throw new Error("_sha2: outputLen bigger than state");
+ for (let i2 = 0; i2 < outLen; i2++)
+ oview.setUint32(4 * i2, state[i2], isLE4);
+ }
+ digest() {
+ const { buffer, outputLen } = this;
+ this.digestInto(buffer);
+ const res = buffer.slice(0, outputLen);
+ this.destroy();
+ return res;
+ }
+ _cloneInto(to) {
+ to || (to = new this.constructor());
+ to.set(...this.get());
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
+ to.length = length;
+ to.pos = pos;
+ to.finished = finished;
+ to.destroyed = destroyed;
+ if (length % blockLen)
+ to.buffer.set(buffer);
+ return to;
+ }
+ };
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/sha256.js
+ var Chi = (a, b, c) => a & b ^ ~a & c;
+ var Maj = (a, b, c) => a & b ^ a & c ^ b & c;
+ var SHA256_K = /* @__PURE__ */ new Uint32Array([
+ 1116352408,
+ 1899447441,
+ 3049323471,
+ 3921009573,
+ 961987163,
+ 1508970993,
+ 2453635748,
+ 2870763221,
+ 3624381080,
+ 310598401,
+ 607225278,
+ 1426881987,
+ 1925078388,
+ 2162078206,
+ 2614888103,
+ 3248222580,
+ 3835390401,
+ 4022224774,
+ 264347078,
+ 604807628,
+ 770255983,
+ 1249150122,
+ 1555081692,
+ 1996064986,
+ 2554220882,
+ 2821834349,
+ 2952996808,
+ 3210313671,
+ 3336571891,
+ 3584528711,
+ 113926993,
+ 338241895,
+ 666307205,
+ 773529912,
+ 1294757372,
+ 1396182291,
+ 1695183700,
+ 1986661051,
+ 2177026350,
+ 2456956037,
+ 2730485921,
+ 2820302411,
+ 3259730800,
+ 3345764771,
+ 3516065817,
+ 3600352804,
+ 4094571909,
+ 275423344,
+ 430227734,
+ 506948616,
+ 659060556,
+ 883997877,
+ 958139571,
+ 1322822218,
+ 1537002063,
+ 1747873779,
+ 1955562222,
+ 2024104815,
+ 2227730452,
+ 2361852424,
+ 2428436474,
+ 2756734187,
+ 3204031479,
+ 3329325298
+ ]);
+ var IV = /* @__PURE__ */ new Uint32Array([
+ 1779033703,
+ 3144134277,
+ 1013904242,
+ 2773480762,
+ 1359893119,
+ 2600822924,
+ 528734635,
+ 1541459225
+ ]);
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
+ var SHA256 = class extends SHA2 {
+ constructor() {
+ super(64, 32, 8, false);
+ this.A = IV[0] | 0;
+ this.B = IV[1] | 0;
+ this.C = IV[2] | 0;
+ this.D = IV[3] | 0;
+ this.E = IV[4] | 0;
+ this.F = IV[5] | 0;
+ this.G = IV[6] | 0;
+ this.H = IV[7] | 0;
+ }
+ get() {
+ const { A, B, C, D, E, F, G, H } = this;
+ return [A, B, C, D, E, F, G, H];
+ }
+ set(A, B, C, D, E, F, G, H) {
+ this.A = A | 0;
+ this.B = B | 0;
+ this.C = C | 0;
+ this.D = D | 0;
+ this.E = E | 0;
+ this.F = F | 0;
+ this.G = G | 0;
+ this.H = H | 0;
+ }
+ process(view, offset) {
+ for (let i2 = 0; i2 < 16; i2++, offset += 4)
+ SHA256_W[i2] = view.getUint32(offset, false);
+ for (let i2 = 16; i2 < 64; i2++) {
+ const W15 = SHA256_W[i2 - 15];
+ const W2 = SHA256_W[i2 - 2];
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
+ SHA256_W[i2] = s1 + SHA256_W[i2 - 7] + s0 + SHA256_W[i2 - 16] | 0;
+ }
+ let { A, B, C, D, E, F, G, H } = this;
+ for (let i2 = 0; i2 < 64; i2++) {
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i2] + SHA256_W[i2] | 0;
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
+ const T2 = sigma0 + Maj(A, B, C) | 0;
+ H = G;
+ G = F;
+ F = E;
+ E = D + T1 | 0;
+ D = C;
+ C = B;
+ B = A;
+ A = T1 + T2 | 0;
+ }
+ A = A + this.A | 0;
+ B = B + this.B | 0;
+ C = C + this.C | 0;
+ D = D + this.D | 0;
+ E = E + this.E | 0;
+ F = F + this.F | 0;
+ G = G + this.G | 0;
+ H = H + this.H | 0;
+ this.set(A, B, C, D, E, F, G, H);
+ }
+ roundClean() {
+ SHA256_W.fill(0);
+ }
+ destroy() {
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
+ this.buffer.fill(0);
+ }
+ };
+ var sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256());
+
+ // node_modules/@noble/curves/esm/abstract/utils.js
+ var utils_exports = {};
+ __export(utils_exports, {
+ bitGet: () => bitGet,
+ bitLen: () => bitLen,
+ bitMask: () => bitMask,
+ bitSet: () => bitSet,
+ bytesToHex: () => bytesToHex,
+ bytesToNumberBE: () => bytesToNumberBE,
+ bytesToNumberLE: () => bytesToNumberLE,
+ concatBytes: () => concatBytes2,
+ createHmacDrbg: () => createHmacDrbg,
+ ensureBytes: () => ensureBytes,
+ equalBytes: () => equalBytes,
+ hexToBytes: () => hexToBytes,
+ hexToNumber: () => hexToNumber,
+ numberToBytesBE: () => numberToBytesBE,
+ numberToBytesLE: () => numberToBytesLE,
+ numberToHexUnpadded: () => numberToHexUnpadded,
+ numberToVarBytesBE: () => numberToVarBytesBE,
+ utf8ToBytes: () => utf8ToBytes2,
+ validateObject: () => validateObject
+ });
+ var _0n = BigInt(0);
+ var _1n = BigInt(1);
+ var _2n = BigInt(2);
+ var u8a2 = (a) => a instanceof Uint8Array;
+ var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i2) => i2.toString(16).padStart(2, "0"));
+ function bytesToHex(bytes4) {
+ if (!u8a2(bytes4))
+ throw new Error("Uint8Array expected");
+ let hex2 = "";
+ for (let i2 = 0; i2 < bytes4.length; i2++) {
+ hex2 += hexes[bytes4[i2]];
+ }
+ return hex2;
+ }
+ function numberToHexUnpadded(num) {
+ const hex2 = num.toString(16);
+ return hex2.length & 1 ? `0${hex2}` : hex2;
+ }
+ function hexToNumber(hex2) {
+ if (typeof hex2 !== "string")
+ throw new Error("hex string expected, got " + typeof hex2);
+ return BigInt(hex2 === "" ? "0" : `0x${hex2}`);
+ }
+ function hexToBytes(hex2) {
+ if (typeof hex2 !== "string")
+ throw new Error("hex string expected, got " + typeof hex2);
+ const len = hex2.length;
+ if (len % 2)
+ throw new Error("padded hex string expected, got unpadded hex of length " + len);
+ const array = new Uint8Array(len / 2);
+ for (let i2 = 0; i2 < array.length; i2++) {
+ const j = i2 * 2;
+ const hexByte = hex2.slice(j, j + 2);
+ const byte = Number.parseInt(hexByte, 16);
+ if (Number.isNaN(byte) || byte < 0)
+ throw new Error("Invalid byte sequence");
+ array[i2] = byte;
+ }
+ return array;
+ }
+ function bytesToNumberBE(bytes4) {
+ return hexToNumber(bytesToHex(bytes4));
+ }
+ function bytesToNumberLE(bytes4) {
+ if (!u8a2(bytes4))
+ throw new Error("Uint8Array expected");
+ return hexToNumber(bytesToHex(Uint8Array.from(bytes4).reverse()));
+ }
+ function numberToBytesBE(n, len) {
+ return hexToBytes(n.toString(16).padStart(len * 2, "0"));
+ }
+ function numberToBytesLE(n, len) {
+ return numberToBytesBE(n, len).reverse();
+ }
+ function numberToVarBytesBE(n) {
+ return hexToBytes(numberToHexUnpadded(n));
+ }
+ function ensureBytes(title, hex2, expectedLength) {
+ let res;
+ if (typeof hex2 === "string") {
+ try {
+ res = hexToBytes(hex2);
+ } catch (e) {
+ throw new Error(`${title} must be valid hex string, got "${hex2}". Cause: ${e}`);
+ }
+ } else if (u8a2(hex2)) {
+ res = Uint8Array.from(hex2);
+ } else {
+ throw new Error(`${title} must be hex string or Uint8Array`);
+ }
+ const len = res.length;
+ if (typeof expectedLength === "number" && len !== expectedLength)
+ throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
+ return res;
+ }
+ function concatBytes2(...arrays) {
+ const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
+ let pad2 = 0;
+ arrays.forEach((a) => {
+ if (!u8a2(a))
+ throw new Error("Uint8Array expected");
+ r.set(a, pad2);
+ pad2 += a.length;
+ });
+ return r;
+ }
+ function equalBytes(b1, b2) {
+ if (b1.length !== b2.length)
+ return false;
+ for (let i2 = 0; i2 < b1.length; i2++)
+ if (b1[i2] !== b2[i2])
+ return false;
+ return true;
+ }
+ function utf8ToBytes2(str) {
+ if (typeof str !== "string")
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
+ return new Uint8Array(new TextEncoder().encode(str));
+ }
+ function bitLen(n) {
+ let len;
+ for (len = 0; n > _0n; n >>= _1n, len += 1)
+ ;
+ return len;
+ }
+ function bitGet(n, pos) {
+ return n >> BigInt(pos) & _1n;
+ }
+ var bitSet = (n, pos, value) => {
+ return n | (value ? _1n : _0n) << BigInt(pos);
+ };
+ var bitMask = (n) => (_2n << BigInt(n - 1)) - _1n;
+ var u8n = (data) => new Uint8Array(data);
+ var u8fr = (arr) => Uint8Array.from(arr);
+ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
+ if (typeof hashLen !== "number" || hashLen < 2)
+ throw new Error("hashLen must be a number");
+ if (typeof qByteLen !== "number" || qByteLen < 2)
+ throw new Error("qByteLen must be a number");
+ if (typeof hmacFn !== "function")
+ throw new Error("hmacFn must be a function");
+ let v = u8n(hashLen);
+ let k = u8n(hashLen);
+ let i2 = 0;
+ const reset = () => {
+ v.fill(1);
+ k.fill(0);
+ i2 = 0;
+ };
+ const h = (...b) => hmacFn(k, v, ...b);
+ const reseed = (seed = u8n()) => {
+ k = h(u8fr([0]), seed);
+ v = h();
+ if (seed.length === 0)
+ return;
+ k = h(u8fr([1]), seed);
+ v = h();
+ };
+ const gen = () => {
+ if (i2++ >= 1e3)
+ throw new Error("drbg: tried 1000 values");
+ let len = 0;
+ const out = [];
+ while (len < qByteLen) {
+ v = h();
+ const sl = v.slice();
+ out.push(sl);
+ len += v.length;
+ }
+ return concatBytes2(...out);
+ };
+ const genUntil = (seed, pred) => {
+ reset();
+ reseed(seed);
+ let res = void 0;
+ while (!(res = pred(gen())))
+ reseed();
+ reset();
+ return res;
+ };
+ return genUntil;
+ }
+ var validatorFns = {
+ bigint: (val) => typeof val === "bigint",
+ function: (val) => typeof val === "function",
+ boolean: (val) => typeof val === "boolean",
+ string: (val) => typeof val === "string",
+ stringOrUint8Array: (val) => typeof val === "string" || val instanceof Uint8Array,
+ isSafeInteger: (val) => Number.isSafeInteger(val),
+ array: (val) => Array.isArray(val),
+ field: (val, object) => object.Fp.isValid(val),
+ hash: (val) => typeof val === "function" && Number.isSafeInteger(val.outputLen)
+ };
+ function validateObject(object, validators, optValidators = {}) {
+ const checkField = (fieldName, type, isOptional) => {
+ const checkVal = validatorFns[type];
+ if (typeof checkVal !== "function")
+ throw new Error(`Invalid validator "${type}", expected function`);
+ const val = object[fieldName];
+ if (isOptional && val === void 0)
+ return;
+ if (!checkVal(val, object)) {
+ throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`);
+ }
+ };
+ for (const [fieldName, type] of Object.entries(validators))
+ checkField(fieldName, type, false);
+ for (const [fieldName, type] of Object.entries(optValidators))
+ checkField(fieldName, type, true);
+ return object;
+ }
+
+ // node_modules/@noble/curves/esm/abstract/modular.js
+ var _0n2 = BigInt(0);
+ var _1n2 = BigInt(1);
+ var _2n2 = BigInt(2);
+ var _3n = BigInt(3);
+ var _4n = BigInt(4);
+ var _5n = BigInt(5);
+ var _8n = BigInt(8);
+ var _9n = BigInt(9);
+ var _16n = BigInt(16);
+ function mod(a, b) {
+ const result = a % b;
+ return result >= _0n2 ? result : b + result;
+ }
+ function pow(num, power, modulo) {
+ if (modulo <= _0n2 || power < _0n2)
+ throw new Error("Expected power/modulo > 0");
+ if (modulo === _1n2)
+ return _0n2;
+ let res = _1n2;
+ while (power > _0n2) {
+ if (power & _1n2)
+ res = res * num % modulo;
+ num = num * num % modulo;
+ power >>= _1n2;
+ }
+ return res;
+ }
+ function pow2(x, power, modulo) {
+ let res = x;
+ while (power-- > _0n2) {
+ res *= res;
+ res %= modulo;
+ }
+ return res;
+ }
+ function invert(number4, modulo) {
+ if (number4 === _0n2 || modulo <= _0n2) {
+ throw new Error(`invert: expected positive integers, got n=${number4} mod=${modulo}`);
+ }
+ let a = mod(number4, modulo);
+ let b = modulo;
+ let x = _0n2, y = _1n2, u = _1n2, v = _0n2;
+ while (a !== _0n2) {
+ const q = b / a;
+ const r = b % a;
+ const m = x - u * q;
+ const n = y - v * q;
+ b = a, a = r, x = u, y = v, u = m, v = n;
+ }
+ const gcd2 = b;
+ if (gcd2 !== _1n2)
+ throw new Error("invert: does not exist");
+ return mod(x, modulo);
+ }
+ function tonelliShanks(P) {
+ const legendreC = (P - _1n2) / _2n2;
+ let Q, S, Z;
+ for (Q = P - _1n2, S = 0; Q % _2n2 === _0n2; Q /= _2n2, S++)
+ ;
+ for (Z = _2n2; Z < P && pow(Z, legendreC, P) !== P - _1n2; Z++)
+ ;
+ if (S === 1) {
+ const p1div4 = (P + _1n2) / _4n;
+ return function tonelliFast(Fp3, n) {
+ const root = Fp3.pow(n, p1div4);
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ const Q1div2 = (Q + _1n2) / _2n2;
+ return function tonelliSlow(Fp3, n) {
+ if (Fp3.pow(n, legendreC) === Fp3.neg(Fp3.ONE))
+ throw new Error("Cannot find square root");
+ let r = S;
+ let g = Fp3.pow(Fp3.mul(Fp3.ONE, Z), Q);
+ let x = Fp3.pow(n, Q1div2);
+ let b = Fp3.pow(n, Q);
+ while (!Fp3.eql(b, Fp3.ONE)) {
+ if (Fp3.eql(b, Fp3.ZERO))
+ return Fp3.ZERO;
+ let m = 1;
+ for (let t2 = Fp3.sqr(b); m < r; m++) {
+ if (Fp3.eql(t2, Fp3.ONE))
+ break;
+ t2 = Fp3.sqr(t2);
+ }
+ const ge2 = Fp3.pow(g, _1n2 << BigInt(r - m - 1));
+ g = Fp3.sqr(ge2);
+ x = Fp3.mul(x, ge2);
+ b = Fp3.mul(b, g);
+ r = m;
+ }
+ return x;
+ };
+ }
+ function FpSqrt(P) {
+ if (P % _4n === _3n) {
+ const p1div4 = (P + _1n2) / _4n;
+ return function sqrt3mod4(Fp3, n) {
+ const root = Fp3.pow(n, p1div4);
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ if (P % _8n === _5n) {
+ const c1 = (P - _5n) / _8n;
+ return function sqrt5mod8(Fp3, n) {
+ const n2 = Fp3.mul(n, _2n2);
+ const v = Fp3.pow(n2, c1);
+ const nv = Fp3.mul(n, v);
+ const i2 = Fp3.mul(Fp3.mul(nv, _2n2), v);
+ const root = Fp3.mul(nv, Fp3.sub(i2, Fp3.ONE));
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ if (P % _16n === _9n) {
+ }
+ return tonelliShanks(P);
+ }
+ var FIELD_FIELDS = [
+ "create",
+ "isValid",
+ "is0",
+ "neg",
+ "inv",
+ "sqrt",
+ "sqr",
+ "eql",
+ "add",
+ "sub",
+ "mul",
+ "pow",
+ "div",
+ "addN",
+ "subN",
+ "mulN",
+ "sqrN"
+ ];
+ function validateField(field) {
+ const initial = {
+ ORDER: "bigint",
+ MASK: "bigint",
+ BYTES: "isSafeInteger",
+ BITS: "isSafeInteger"
+ };
+ const opts = FIELD_FIELDS.reduce((map, val) => {
+ map[val] = "function";
+ return map;
+ }, initial);
+ return validateObject(field, opts);
+ }
+ function FpPow(f2, num, power) {
+ if (power < _0n2)
+ throw new Error("Expected power > 0");
+ if (power === _0n2)
+ return f2.ONE;
+ if (power === _1n2)
+ return num;
+ let p = f2.ONE;
+ let d = num;
+ while (power > _0n2) {
+ if (power & _1n2)
+ p = f2.mul(p, d);
+ d = f2.sqr(d);
+ power >>= _1n2;
+ }
+ return p;
+ }
+ function FpInvertBatch(f2, nums) {
+ const tmp = new Array(nums.length);
+ const lastMultiplied = nums.reduce((acc, num, i2) => {
+ if (f2.is0(num))
+ return acc;
+ tmp[i2] = acc;
+ return f2.mul(acc, num);
+ }, f2.ONE);
+ const inverted = f2.inv(lastMultiplied);
+ nums.reduceRight((acc, num, i2) => {
+ if (f2.is0(num))
+ return acc;
+ tmp[i2] = f2.mul(acc, tmp[i2]);
+ return f2.mul(acc, num);
+ }, inverted);
+ return tmp;
+ }
+ function nLength(n, nBitLength) {
+ const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length;
+ const nByteLength = Math.ceil(_nBitLength / 8);
+ return { nBitLength: _nBitLength, nByteLength };
+ }
+ function Field(ORDER, bitLen3, isLE4 = false, redef = {}) {
+ if (ORDER <= _0n2)
+ throw new Error(`Expected Field ORDER > 0, got ${ORDER}`);
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen3);
+ if (BYTES > 2048)
+ throw new Error("Field lengths over 2048 bytes are not supported");
+ const sqrtP = FpSqrt(ORDER);
+ const f2 = Object.freeze({
+ ORDER,
+ BITS,
+ BYTES,
+ MASK: bitMask(BITS),
+ ZERO: _0n2,
+ ONE: _1n2,
+ create: (num) => mod(num, ORDER),
+ isValid: (num) => {
+ if (typeof num !== "bigint")
+ throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
+ return _0n2 <= num && num < ORDER;
+ },
+ is0: (num) => num === _0n2,
+ isOdd: (num) => (num & _1n2) === _1n2,
+ neg: (num) => mod(-num, ORDER),
+ eql: (lhs, rhs) => lhs === rhs,
+ sqr: (num) => mod(num * num, ORDER),
+ add: (lhs, rhs) => mod(lhs + rhs, ORDER),
+ sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
+ mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
+ pow: (num, power) => FpPow(f2, num, power),
+ div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
+ sqrN: (num) => num * num,
+ addN: (lhs, rhs) => lhs + rhs,
+ subN: (lhs, rhs) => lhs - rhs,
+ mulN: (lhs, rhs) => lhs * rhs,
+ inv: (num) => invert(num, ORDER),
+ sqrt: redef.sqrt || ((n) => sqrtP(f2, n)),
+ invertBatch: (lst) => FpInvertBatch(f2, lst),
+ cmov: (a, b, c) => c ? b : a,
+ toBytes: (num) => isLE4 ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES),
+ fromBytes: (bytes4) => {
+ if (bytes4.length !== BYTES)
+ throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes4.length}`);
+ return isLE4 ? bytesToNumberLE(bytes4) : bytesToNumberBE(bytes4);
+ }
+ });
+ return Object.freeze(f2);
+ }
+ function getFieldBytesLength(fieldOrder) {
+ if (typeof fieldOrder !== "bigint")
+ throw new Error("field order must be bigint");
+ const bitLength = fieldOrder.toString(2).length;
+ return Math.ceil(bitLength / 8);
+ }
+ function getMinHashLength(fieldOrder) {
+ const length = getFieldBytesLength(fieldOrder);
+ return length + Math.ceil(length / 2);
+ }
+ function mapHashToField(key, fieldOrder, isLE4 = false) {
+ const len = key.length;
+ const fieldLen = getFieldBytesLength(fieldOrder);
+ const minLen = getMinHashLength(fieldOrder);
+ if (len < 16 || len < minLen || len > 1024)
+ throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`);
+ const num = isLE4 ? bytesToNumberBE(key) : bytesToNumberLE(key);
+ const reduced = mod(num, fieldOrder - _1n2) + _1n2;
+ return isLE4 ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
+ }
+
+ // node_modules/@noble/curves/esm/abstract/curve.js
+ var _0n3 = BigInt(0);
+ var _1n3 = BigInt(1);
+ function wNAF(c, bits) {
+ const constTimeNegate = (condition, item) => {
+ const neg = item.negate();
+ return condition ? neg : item;
+ };
+ const opts = (W) => {
+ const windows = Math.ceil(bits / W) + 1;
+ const windowSize = 2 ** (W - 1);
+ return { windows, windowSize };
+ };
+ return {
+ constTimeNegate,
+ unsafeLadder(elm, n) {
+ let p = c.ZERO;
+ let d = elm;
+ while (n > _0n3) {
+ if (n & _1n3)
+ p = p.add(d);
+ d = d.double();
+ n >>= _1n3;
+ }
+ return p;
+ },
+ precomputeWindow(elm, W) {
+ const { windows, windowSize } = opts(W);
+ const points = [];
+ let p = elm;
+ let base = p;
+ for (let window = 0; window < windows; window++) {
+ base = p;
+ points.push(base);
+ for (let i2 = 1; i2 < windowSize; i2++) {
+ base = base.add(p);
+ points.push(base);
+ }
+ p = base.double();
+ }
+ return points;
+ },
+ wNAF(W, precomputes, n) {
+ const { windows, windowSize } = opts(W);
+ let p = c.ZERO;
+ let f2 = c.BASE;
+ const mask = BigInt(2 ** W - 1);
+ const maxNumber = 2 ** W;
+ const shiftBy = BigInt(W);
+ for (let window = 0; window < windows; window++) {
+ const offset = window * windowSize;
+ let wbits = Number(n & mask);
+ n >>= shiftBy;
+ if (wbits > windowSize) {
+ wbits -= maxNumber;
+ n += _1n3;
+ }
+ const offset1 = offset;
+ const offset2 = offset + Math.abs(wbits) - 1;
+ const cond1 = window % 2 !== 0;
+ const cond2 = wbits < 0;
+ if (wbits === 0) {
+ f2 = f2.add(constTimeNegate(cond1, precomputes[offset1]));
+ } else {
+ p = p.add(constTimeNegate(cond2, precomputes[offset2]));
+ }
+ }
+ return { p, f: f2 };
+ },
+ wNAFCached(P, precomputesMap, n, transform) {
+ const W = P._WINDOW_SIZE || 1;
+ let comp = precomputesMap.get(P);
+ if (!comp) {
+ comp = this.precomputeWindow(P, W);
+ if (W !== 1) {
+ precomputesMap.set(P, transform(comp));
+ }
+ }
+ return this.wNAF(W, comp, n);
+ }
+ };
+ }
+ function validateBasic(curve) {
+ validateField(curve.Fp);
+ validateObject(curve, {
+ n: "bigint",
+ h: "bigint",
+ Gx: "field",
+ Gy: "field"
+ }, {
+ nBitLength: "isSafeInteger",
+ nByteLength: "isSafeInteger"
+ });
+ return Object.freeze({
+ ...nLength(curve.n, curve.nBitLength),
+ ...curve,
+ ...{ p: curve.Fp.ORDER }
+ });
+ }
+
+ // node_modules/@noble/curves/esm/abstract/weierstrass.js
+ function validatePointOpts(curve) {
+ const opts = validateBasic(curve);
+ validateObject(opts, {
+ a: "field",
+ b: "field"
+ }, {
+ allowedPrivateKeyLengths: "array",
+ wrapPrivateKey: "boolean",
+ isTorsionFree: "function",
+ clearCofactor: "function",
+ allowInfinityPoint: "boolean",
+ fromBytes: "function",
+ toBytes: "function"
+ });
+ const { endo, Fp: Fp3, a } = opts;
+ if (endo) {
+ if (!Fp3.eql(a, Fp3.ZERO)) {
+ throw new Error("Endomorphism can only be defined for Koblitz curves that have a=0");
+ }
+ if (typeof endo !== "object" || typeof endo.beta !== "bigint" || typeof endo.splitScalar !== "function") {
+ throw new Error("Expected endomorphism with beta: bigint and splitScalar: function");
+ }
+ }
+ return Object.freeze({ ...opts });
+ }
+ var { bytesToNumberBE: b2n, hexToBytes: h2b } = utils_exports;
+ var DER = {
+ Err: class DERErr extends Error {
+ constructor(m = "") {
+ super(m);
+ }
+ },
+ _parseInt(data) {
+ const { Err: E } = DER;
+ if (data.length < 2 || data[0] !== 2)
+ throw new E("Invalid signature integer tag");
+ const len = data[1];
+ const res = data.subarray(2, len + 2);
+ if (!len || res.length !== len)
+ throw new E("Invalid signature integer: wrong length");
+ if (res[0] & 128)
+ throw new E("Invalid signature integer: negative");
+ if (res[0] === 0 && !(res[1] & 128))
+ throw new E("Invalid signature integer: unnecessary leading zero");
+ return { d: b2n(res), l: data.subarray(len + 2) };
+ },
+ toSig(hex2) {
+ const { Err: E } = DER;
+ const data = typeof hex2 === "string" ? h2b(hex2) : hex2;
+ if (!(data instanceof Uint8Array))
+ throw new Error("ui8a expected");
+ let l = data.length;
+ if (l < 2 || data[0] != 48)
+ throw new E("Invalid signature tag");
+ if (data[1] !== l - 2)
+ throw new E("Invalid signature: incorrect length");
+ const { d: r, l: sBytes } = DER._parseInt(data.subarray(2));
+ const { d: s, l: rBytesLeft } = DER._parseInt(sBytes);
+ if (rBytesLeft.length)
+ throw new E("Invalid signature: left bytes after parsing");
+ return { r, s };
+ },
+ hexFromSig(sig) {
+ const slice = (s2) => Number.parseInt(s2[0], 16) & 8 ? "00" + s2 : s2;
+ const h = (num) => {
+ const hex2 = num.toString(16);
+ return hex2.length & 1 ? `0${hex2}` : hex2;
+ };
+ const s = slice(h(sig.s));
+ const r = slice(h(sig.r));
+ const shl = s.length / 2;
+ const rhl = r.length / 2;
+ const sl = h(shl);
+ const rl = h(rhl);
+ return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
+ }
+ };
+ var _0n4 = BigInt(0);
+ var _1n4 = BigInt(1);
+ var _2n3 = BigInt(2);
+ var _3n2 = BigInt(3);
+ var _4n2 = BigInt(4);
+ function weierstrassPoints(opts) {
+ const CURVE = validatePointOpts(opts);
+ const { Fp: Fp3 } = CURVE;
+ const toBytes4 = CURVE.toBytes || ((_c, point, _isCompressed) => {
+ const a = point.toAffine();
+ return concatBytes2(Uint8Array.from([4]), Fp3.toBytes(a.x), Fp3.toBytes(a.y));
+ });
+ const fromBytes = CURVE.fromBytes || ((bytes4) => {
+ const tail = bytes4.subarray(1);
+ const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES));
+ const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES));
+ return { x, y };
+ });
+ function weierstrassEquation(x) {
+ const { a, b } = CURVE;
+ const x2 = Fp3.sqr(x);
+ const x3 = Fp3.mul(x2, x);
+ return Fp3.add(Fp3.add(x3, Fp3.mul(x, a)), b);
+ }
+ if (!Fp3.eql(Fp3.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
+ throw new Error("bad generator point: equation left != right");
+ function isWithinCurveOrder(num) {
+ return typeof num === "bigint" && _0n4 < num && num < CURVE.n;
+ }
+ function assertGE(num) {
+ if (!isWithinCurveOrder(num))
+ throw new Error("Expected valid bigint: 0 < bigint < curve.n");
+ }
+ function normPrivateKeyToScalar(key) {
+ const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
+ if (lengths && typeof key !== "bigint") {
+ if (key instanceof Uint8Array)
+ key = bytesToHex(key);
+ if (typeof key !== "string" || !lengths.includes(key.length))
+ throw new Error("Invalid key");
+ key = key.padStart(nByteLength * 2, "0");
+ }
+ let num;
+ try {
+ num = typeof key === "bigint" ? key : bytesToNumberBE(ensureBytes("private key", key, nByteLength));
+ } catch (error) {
+ throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
+ }
+ if (wrapPrivateKey)
+ num = mod(num, n);
+ assertGE(num);
+ return num;
+ }
+ const pointPrecomputes = /* @__PURE__ */ new Map();
+ function assertPrjPoint(other) {
+ if (!(other instanceof Point4))
+ throw new Error("ProjectivePoint expected");
+ }
+ class Point4 {
+ constructor(px, py, pz) {
+ this.px = px;
+ this.py = py;
+ this.pz = pz;
+ if (px == null || !Fp3.isValid(px))
+ throw new Error("x required");
+ if (py == null || !Fp3.isValid(py))
+ throw new Error("y required");
+ if (pz == null || !Fp3.isValid(pz))
+ throw new Error("z required");
+ }
+ static fromAffine(p) {
+ const { x, y } = p || {};
+ if (!p || !Fp3.isValid(x) || !Fp3.isValid(y))
+ throw new Error("invalid affine point");
+ if (p instanceof Point4)
+ throw new Error("projective point not allowed");
+ const is0 = (i2) => Fp3.eql(i2, Fp3.ZERO);
+ if (is0(x) && is0(y))
+ return Point4.ZERO;
+ return new Point4(x, y, Fp3.ONE);
+ }
+ get x() {
+ return this.toAffine().x;
+ }
+ get y() {
+ return this.toAffine().y;
+ }
+ static normalizeZ(points) {
+ const toInv = Fp3.invertBatch(points.map((p) => p.pz));
+ return points.map((p, i2) => p.toAffine(toInv[i2])).map(Point4.fromAffine);
+ }
+ static fromHex(hex2) {
+ const P = Point4.fromAffine(fromBytes(ensureBytes("pointHex", hex2)));
+ P.assertValidity();
+ return P;
+ }
+ static fromPrivateKey(privateKey) {
+ return Point4.BASE.multiply(normPrivateKeyToScalar(privateKey));
+ }
+ _setWindowSize(windowSize) {
+ this._WINDOW_SIZE = windowSize;
+ pointPrecomputes.delete(this);
+ }
+ assertValidity() {
+ if (this.is0()) {
+ if (CURVE.allowInfinityPoint && !Fp3.is0(this.py))
+ return;
+ throw new Error("bad point: ZERO");
+ }
+ const { x, y } = this.toAffine();
+ if (!Fp3.isValid(x) || !Fp3.isValid(y))
+ throw new Error("bad point: x or y not FE");
+ const left = Fp3.sqr(y);
+ const right = weierstrassEquation(x);
+ if (!Fp3.eql(left, right))
+ throw new Error("bad point: equation left != right");
+ if (!this.isTorsionFree())
+ throw new Error("bad point: not in prime-order subgroup");
+ }
+ hasEvenY() {
+ const { y } = this.toAffine();
+ if (Fp3.isOdd)
+ return !Fp3.isOdd(y);
+ throw new Error("Field doesn't support isOdd");
+ }
+ equals(other) {
+ assertPrjPoint(other);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ const { px: X2, py: Y2, pz: Z2 } = other;
+ const U1 = Fp3.eql(Fp3.mul(X1, Z2), Fp3.mul(X2, Z1));
+ const U2 = Fp3.eql(Fp3.mul(Y1, Z2), Fp3.mul(Y2, Z1));
+ return U1 && U2;
+ }
+ negate() {
+ return new Point4(this.px, Fp3.neg(this.py), this.pz);
+ }
+ double() {
+ const { a, b } = CURVE;
+ const b3 = Fp3.mul(b, _3n2);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO;
+ let t0 = Fp3.mul(X1, X1);
+ let t1 = Fp3.mul(Y1, Y1);
+ let t2 = Fp3.mul(Z1, Z1);
+ let t3 = Fp3.mul(X1, Y1);
+ t3 = Fp3.add(t3, t3);
+ Z3 = Fp3.mul(X1, Z1);
+ Z3 = Fp3.add(Z3, Z3);
+ X3 = Fp3.mul(a, Z3);
+ Y3 = Fp3.mul(b3, t2);
+ Y3 = Fp3.add(X3, Y3);
+ X3 = Fp3.sub(t1, Y3);
+ Y3 = Fp3.add(t1, Y3);
+ Y3 = Fp3.mul(X3, Y3);
+ X3 = Fp3.mul(t3, X3);
+ Z3 = Fp3.mul(b3, Z3);
+ t2 = Fp3.mul(a, t2);
+ t3 = Fp3.sub(t0, t2);
+ t3 = Fp3.mul(a, t3);
+ t3 = Fp3.add(t3, Z3);
+ Z3 = Fp3.add(t0, t0);
+ t0 = Fp3.add(Z3, t0);
+ t0 = Fp3.add(t0, t2);
+ t0 = Fp3.mul(t0, t3);
+ Y3 = Fp3.add(Y3, t0);
+ t2 = Fp3.mul(Y1, Z1);
+ t2 = Fp3.add(t2, t2);
+ t0 = Fp3.mul(t2, t3);
+ X3 = Fp3.sub(X3, t0);
+ Z3 = Fp3.mul(t2, t1);
+ Z3 = Fp3.add(Z3, Z3);
+ Z3 = Fp3.add(Z3, Z3);
+ return new Point4(X3, Y3, Z3);
+ }
+ add(other) {
+ assertPrjPoint(other);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ const { px: X2, py: Y2, pz: Z2 } = other;
+ let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO;
+ const a = CURVE.a;
+ const b3 = Fp3.mul(CURVE.b, _3n2);
+ let t0 = Fp3.mul(X1, X2);
+ let t1 = Fp3.mul(Y1, Y2);
+ let t2 = Fp3.mul(Z1, Z2);
+ let t3 = Fp3.add(X1, Y1);
+ let t4 = Fp3.add(X2, Y2);
+ t3 = Fp3.mul(t3, t4);
+ t4 = Fp3.add(t0, t1);
+ t3 = Fp3.sub(t3, t4);
+ t4 = Fp3.add(X1, Z1);
+ let t5 = Fp3.add(X2, Z2);
+ t4 = Fp3.mul(t4, t5);
+ t5 = Fp3.add(t0, t2);
+ t4 = Fp3.sub(t4, t5);
+ t5 = Fp3.add(Y1, Z1);
+ X3 = Fp3.add(Y2, Z2);
+ t5 = Fp3.mul(t5, X3);
+ X3 = Fp3.add(t1, t2);
+ t5 = Fp3.sub(t5, X3);
+ Z3 = Fp3.mul(a, t4);
+ X3 = Fp3.mul(b3, t2);
+ Z3 = Fp3.add(X3, Z3);
+ X3 = Fp3.sub(t1, Z3);
+ Z3 = Fp3.add(t1, Z3);
+ Y3 = Fp3.mul(X3, Z3);
+ t1 = Fp3.add(t0, t0);
+ t1 = Fp3.add(t1, t0);
+ t2 = Fp3.mul(a, t2);
+ t4 = Fp3.mul(b3, t4);
+ t1 = Fp3.add(t1, t2);
+ t2 = Fp3.sub(t0, t2);
+ t2 = Fp3.mul(a, t2);
+ t4 = Fp3.add(t4, t2);
+ t0 = Fp3.mul(t1, t4);
+ Y3 = Fp3.add(Y3, t0);
+ t0 = Fp3.mul(t5, t4);
+ X3 = Fp3.mul(t3, X3);
+ X3 = Fp3.sub(X3, t0);
+ t0 = Fp3.mul(t3, t1);
+ Z3 = Fp3.mul(t5, Z3);
+ Z3 = Fp3.add(Z3, t0);
+ return new Point4(X3, Y3, Z3);
+ }
+ subtract(other) {
+ return this.add(other.negate());
+ }
+ is0() {
+ return this.equals(Point4.ZERO);
+ }
+ wNAF(n) {
+ return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
+ const toInv = Fp3.invertBatch(comp.map((p) => p.pz));
+ return comp.map((p, i2) => p.toAffine(toInv[i2])).map(Point4.fromAffine);
+ });
+ }
+ multiplyUnsafe(n) {
+ const I = Point4.ZERO;
+ if (n === _0n4)
+ return I;
+ assertGE(n);
+ if (n === _1n4)
+ return this;
+ const { endo } = CURVE;
+ if (!endo)
+ return wnaf.unsafeLadder(this, n);
+ let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
+ let k1p = I;
+ let k2p = I;
+ let d = this;
+ while (k1 > _0n4 || k2 > _0n4) {
+ if (k1 & _1n4)
+ k1p = k1p.add(d);
+ if (k2 & _1n4)
+ k2p = k2p.add(d);
+ d = d.double();
+ k1 >>= _1n4;
+ k2 >>= _1n4;
+ }
+ if (k1neg)
+ k1p = k1p.negate();
+ if (k2neg)
+ k2p = k2p.negate();
+ k2p = new Point4(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
+ return k1p.add(k2p);
+ }
+ multiply(scalar) {
+ assertGE(scalar);
+ let n = scalar;
+ let point, fake;
+ const { endo } = CURVE;
+ if (endo) {
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
+ let { p: k1p, f: f1p } = this.wNAF(k1);
+ let { p: k2p, f: f2p } = this.wNAF(k2);
+ k1p = wnaf.constTimeNegate(k1neg, k1p);
+ k2p = wnaf.constTimeNegate(k2neg, k2p);
+ k2p = new Point4(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
+ point = k1p.add(k2p);
+ fake = f1p.add(f2p);
+ } else {
+ const { p, f: f2 } = this.wNAF(n);
+ point = p;
+ fake = f2;
+ }
+ return Point4.normalizeZ([point, fake])[0];
+ }
+ multiplyAndAddUnsafe(Q, a, b) {
+ const G = Point4.BASE;
+ const mul3 = (P, a2) => a2 === _0n4 || a2 === _1n4 || !P.equals(G) ? P.multiplyUnsafe(a2) : P.multiply(a2);
+ const sum = mul3(this, a).add(mul3(Q, b));
+ return sum.is0() ? void 0 : sum;
+ }
+ toAffine(iz) {
+ const { px: x, py: y, pz: z } = this;
+ const is0 = this.is0();
+ if (iz == null)
+ iz = is0 ? Fp3.ONE : Fp3.inv(z);
+ const ax = Fp3.mul(x, iz);
+ const ay = Fp3.mul(y, iz);
+ const zz = Fp3.mul(z, iz);
+ if (is0)
+ return { x: Fp3.ZERO, y: Fp3.ZERO };
+ if (!Fp3.eql(zz, Fp3.ONE))
+ throw new Error("invZ was invalid");
+ return { x: ax, y: ay };
+ }
+ isTorsionFree() {
+ const { h: cofactor, isTorsionFree } = CURVE;
+ if (cofactor === _1n4)
+ return true;
+ if (isTorsionFree)
+ return isTorsionFree(Point4, this);
+ throw new Error("isTorsionFree() has not been declared for the elliptic curve");
+ }
+ clearCofactor() {
+ const { h: cofactor, clearCofactor } = CURVE;
+ if (cofactor === _1n4)
+ return this;
+ if (clearCofactor)
+ return clearCofactor(Point4, this);
+ return this.multiplyUnsafe(CURVE.h);
+ }
+ toRawBytes(isCompressed = true) {
+ this.assertValidity();
+ return toBytes4(Point4, this, isCompressed);
+ }
+ toHex(isCompressed = true) {
+ return bytesToHex(this.toRawBytes(isCompressed));
+ }
+ }
+ Point4.BASE = new Point4(CURVE.Gx, CURVE.Gy, Fp3.ONE);
+ Point4.ZERO = new Point4(Fp3.ZERO, Fp3.ONE, Fp3.ZERO);
+ const _bits = CURVE.nBitLength;
+ const wnaf = wNAF(Point4, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
+ return {
+ CURVE,
+ ProjectivePoint: Point4,
+ normPrivateKeyToScalar,
+ weierstrassEquation,
+ isWithinCurveOrder
+ };
+ }
+ function validateOpts(curve) {
+ const opts = validateBasic(curve);
+ validateObject(opts, {
+ hash: "hash",
+ hmac: "function",
+ randomBytes: "function"
+ }, {
+ bits2int: "function",
+ bits2int_modN: "function",
+ lowS: "boolean"
+ });
+ return Object.freeze({ lowS: true, ...opts });
+ }
+ function weierstrass(curveDef) {
+ const CURVE = validateOpts(curveDef);
+ const { Fp: Fp3, n: CURVE_ORDER } = CURVE;
+ const compressedLen = Fp3.BYTES + 1;
+ const uncompressedLen = 2 * Fp3.BYTES + 1;
+ function isValidFieldElement(num) {
+ return _0n4 < num && num < Fp3.ORDER;
+ }
+ function modN2(a) {
+ return mod(a, CURVE_ORDER);
+ }
+ function invN(a) {
+ return invert(a, CURVE_ORDER);
+ }
+ const { ProjectivePoint: Point4, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({
+ ...CURVE,
+ toBytes(_c, point, isCompressed) {
+ const a = point.toAffine();
+ const x = Fp3.toBytes(a.x);
+ const cat = concatBytes2;
+ if (isCompressed) {
+ return cat(Uint8Array.from([point.hasEvenY() ? 2 : 3]), x);
+ } else {
+ return cat(Uint8Array.from([4]), x, Fp3.toBytes(a.y));
+ }
+ },
+ fromBytes(bytes4) {
+ const len = bytes4.length;
+ const head = bytes4[0];
+ const tail = bytes4.subarray(1);
+ if (len === compressedLen && (head === 2 || head === 3)) {
+ const x = bytesToNumberBE(tail);
+ if (!isValidFieldElement(x))
+ throw new Error("Point is not on curve");
+ const y2 = weierstrassEquation(x);
+ let y = Fp3.sqrt(y2);
+ const isYOdd = (y & _1n4) === _1n4;
+ const isHeadOdd = (head & 1) === 1;
+ if (isHeadOdd !== isYOdd)
+ y = Fp3.neg(y);
+ return { x, y };
+ } else if (len === uncompressedLen && head === 4) {
+ const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES));
+ const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES));
+ return { x, y };
+ } else {
+ throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`);
+ }
+ }
+ });
+ const numToNByteStr = (num) => bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
+ function isBiggerThanHalfOrder(number4) {
+ const HALF = CURVE_ORDER >> _1n4;
+ return number4 > HALF;
+ }
+ function normalizeS(s) {
+ return isBiggerThanHalfOrder(s) ? modN2(-s) : s;
+ }
+ const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
+ class Signature {
+ constructor(r, s, recovery) {
+ this.r = r;
+ this.s = s;
+ this.recovery = recovery;
+ this.assertValidity();
+ }
+ static fromCompact(hex2) {
+ const l = CURVE.nByteLength;
+ hex2 = ensureBytes("compactSignature", hex2, l * 2);
+ return new Signature(slcNum(hex2, 0, l), slcNum(hex2, l, 2 * l));
+ }
+ static fromDER(hex2) {
+ const { r, s } = DER.toSig(ensureBytes("DER", hex2));
+ return new Signature(r, s);
+ }
+ assertValidity() {
+ if (!isWithinCurveOrder(this.r))
+ throw new Error("r must be 0 < r < CURVE.n");
+ if (!isWithinCurveOrder(this.s))
+ throw new Error("s must be 0 < s < CURVE.n");
+ }
+ addRecoveryBit(recovery) {
+ return new Signature(this.r, this.s, recovery);
+ }
+ recoverPublicKey(msgHash) {
+ const { r, s, recovery: rec } = this;
+ const h = bits2int_modN(ensureBytes("msgHash", msgHash));
+ if (rec == null || ![0, 1, 2, 3].includes(rec))
+ throw new Error("recovery id invalid");
+ const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
+ if (radj >= Fp3.ORDER)
+ throw new Error("recovery id 2 or 3 invalid");
+ const prefix = (rec & 1) === 0 ? "02" : "03";
+ const R = Point4.fromHex(prefix + numToNByteStr(radj));
+ const ir = invN(radj);
+ const u1 = modN2(-h * ir);
+ const u2 = modN2(s * ir);
+ const Q = Point4.BASE.multiplyAndAddUnsafe(R, u1, u2);
+ if (!Q)
+ throw new Error("point at infinify");
+ Q.assertValidity();
+ return Q;
+ }
+ hasHighS() {
+ return isBiggerThanHalfOrder(this.s);
+ }
+ normalizeS() {
+ return this.hasHighS() ? new Signature(this.r, modN2(-this.s), this.recovery) : this;
+ }
+ toDERRawBytes() {
+ return hexToBytes(this.toDERHex());
+ }
+ toDERHex() {
+ return DER.hexFromSig({ r: this.r, s: this.s });
+ }
+ toCompactRawBytes() {
+ return hexToBytes(this.toCompactHex());
+ }
+ toCompactHex() {
+ return numToNByteStr(this.r) + numToNByteStr(this.s);
+ }
+ }
+ const utils2 = {
+ isValidPrivateKey(privateKey) {
+ try {
+ normPrivateKeyToScalar(privateKey);
+ return true;
+ } catch (error) {
+ return false;
+ }
+ },
+ normPrivateKeyToScalar,
+ randomPrivateKey: () => {
+ const length = getMinHashLength(CURVE.n);
+ return mapHashToField(CURVE.randomBytes(length), CURVE.n);
+ },
+ precompute(windowSize = 8, point = Point4.BASE) {
+ point._setWindowSize(windowSize);
+ point.multiply(BigInt(3));
+ return point;
+ }
+ };
+ function getPublicKey2(privateKey, isCompressed = true) {
+ return Point4.fromPrivateKey(privateKey).toRawBytes(isCompressed);
+ }
+ function isProbPub(item) {
+ const arr = item instanceof Uint8Array;
+ const str = typeof item === "string";
+ const len = (arr || str) && item.length;
+ if (arr)
+ return len === compressedLen || len === uncompressedLen;
+ if (str)
+ return len === 2 * compressedLen || len === 2 * uncompressedLen;
+ if (item instanceof Point4)
+ return true;
+ return false;
+ }
+ function getSharedSecret(privateA, publicB, isCompressed = true) {
+ if (isProbPub(privateA))
+ throw new Error("first arg must be private key");
+ if (!isProbPub(publicB))
+ throw new Error("second arg must be public key");
+ const b = Point4.fromHex(publicB);
+ return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
+ }
+ const bits2int = CURVE.bits2int || function(bytes4) {
+ const num = bytesToNumberBE(bytes4);
+ const delta = bytes4.length * 8 - CURVE.nBitLength;
+ return delta > 0 ? num >> BigInt(delta) : num;
+ };
+ const bits2int_modN = CURVE.bits2int_modN || function(bytes4) {
+ return modN2(bits2int(bytes4));
+ };
+ const ORDER_MASK = bitMask(CURVE.nBitLength);
+ function int2octets(num) {
+ if (typeof num !== "bigint")
+ throw new Error("bigint expected");
+ if (!(_0n4 <= num && num < ORDER_MASK))
+ throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
+ return numberToBytesBE(num, CURVE.nByteLength);
+ }
+ function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
+ if (["recovered", "canonical"].some((k) => k in opts))
+ throw new Error("sign() legacy options not supported");
+ const { hash: hash3, randomBytes: randomBytes3 } = CURVE;
+ let { lowS, prehash, extraEntropy: ent } = opts;
+ if (lowS == null)
+ lowS = true;
+ msgHash = ensureBytes("msgHash", msgHash);
+ if (prehash)
+ msgHash = ensureBytes("prehashed msgHash", hash3(msgHash));
+ const h1int = bits2int_modN(msgHash);
+ const d = normPrivateKeyToScalar(privateKey);
+ const seedArgs = [int2octets(d), int2octets(h1int)];
+ if (ent != null) {
+ const e = ent === true ? randomBytes3(Fp3.BYTES) : ent;
+ seedArgs.push(ensureBytes("extraEntropy", e));
+ }
+ const seed = concatBytes2(...seedArgs);
+ const m = h1int;
+ function k2sig(kBytes) {
+ const k = bits2int(kBytes);
+ if (!isWithinCurveOrder(k))
+ return;
+ const ik = invN(k);
+ const q = Point4.BASE.multiply(k).toAffine();
+ const r = modN2(q.x);
+ if (r === _0n4)
+ return;
+ const s = modN2(ik * modN2(m + r * d));
+ if (s === _0n4)
+ return;
+ let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n4);
+ let normS = s;
+ if (lowS && isBiggerThanHalfOrder(s)) {
+ normS = normalizeS(s);
+ recovery ^= 1;
+ }
+ return new Signature(r, normS, recovery);
+ }
+ return { seed, k2sig };
+ }
+ const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
+ const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
+ function sign(msgHash, privKey, opts = defaultSigOpts) {
+ const { seed, k2sig } = prepSig(msgHash, privKey, opts);
+ const C = CURVE;
+ const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac);
+ return drbg(seed, k2sig);
+ }
+ Point4.BASE._setWindowSize(8);
+ function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
+ const sg = signature;
+ msgHash = ensureBytes("msgHash", msgHash);
+ publicKey = ensureBytes("publicKey", publicKey);
+ if ("strict" in opts)
+ throw new Error("options.strict was renamed to lowS");
+ const { lowS, prehash } = opts;
+ let _sig = void 0;
+ let P;
+ try {
+ if (typeof sg === "string" || sg instanceof Uint8Array) {
+ try {
+ _sig = Signature.fromDER(sg);
+ } catch (derError) {
+ if (!(derError instanceof DER.Err))
+ throw derError;
+ _sig = Signature.fromCompact(sg);
+ }
+ } else if (typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint") {
+ const { r: r2, s: s2 } = sg;
+ _sig = new Signature(r2, s2);
+ } else {
+ throw new Error("PARSE");
+ }
+ P = Point4.fromHex(publicKey);
+ } catch (error) {
+ if (error.message === "PARSE")
+ throw new Error(`signature must be Signature instance, Uint8Array or hex string`);
+ return false;
+ }
+ if (lowS && _sig.hasHighS())
+ return false;
+ if (prehash)
+ msgHash = CURVE.hash(msgHash);
+ const { r, s } = _sig;
+ const h = bits2int_modN(msgHash);
+ const is = invN(s);
+ const u1 = modN2(h * is);
+ const u2 = modN2(r * is);
+ const R = Point4.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine();
+ if (!R)
+ return false;
+ const v = modN2(R.x);
+ return v === r;
+ }
+ return {
+ CURVE,
+ getPublicKey: getPublicKey2,
+ getSharedSecret,
+ sign,
+ verify,
+ ProjectivePoint: Point4,
+ Signature,
+ utils: utils2
+ };
+ }
+
+ // node_modules/@noble/curves/node_modules/@noble/hashes/esm/hmac.js
+ var HMAC = class extends Hash {
+ constructor(hash3, _key) {
+ super();
+ this.finished = false;
+ this.destroyed = false;
+ hash(hash3);
+ const key = toBytes(_key);
+ this.iHash = hash3.create();
+ if (typeof this.iHash.update !== "function")
+ throw new Error("Expected instance of class which extends utils.Hash");
+ this.blockLen = this.iHash.blockLen;
+ this.outputLen = this.iHash.outputLen;
+ const blockLen = this.blockLen;
+ const pad2 = new Uint8Array(blockLen);
+ pad2.set(key.length > blockLen ? hash3.create().update(key).digest() : key);
+ for (let i2 = 0; i2 < pad2.length; i2++)
+ pad2[i2] ^= 54;
+ this.iHash.update(pad2);
+ this.oHash = hash3.create();
+ for (let i2 = 0; i2 < pad2.length; i2++)
+ pad2[i2] ^= 54 ^ 92;
+ this.oHash.update(pad2);
+ pad2.fill(0);
+ }
+ update(buf) {
+ exists(this);
+ this.iHash.update(buf);
+ return this;
+ }
+ digestInto(out) {
+ exists(this);
+ bytes(out, this.outputLen);
+ this.finished = true;
+ this.iHash.digestInto(out);
+ this.oHash.update(out);
+ this.oHash.digestInto(out);
+ this.destroy();
+ }
+ digest() {
+ const out = new Uint8Array(this.oHash.outputLen);
+ this.digestInto(out);
+ return out;
+ }
+ _cloneInto(to) {
+ to || (to = Object.create(Object.getPrototypeOf(this), {}));
+ const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
+ to = to;
+ to.finished = finished;
+ to.destroyed = destroyed;
+ to.blockLen = blockLen;
+ to.outputLen = outputLen;
+ to.oHash = oHash._cloneInto(to.oHash);
+ to.iHash = iHash._cloneInto(to.iHash);
+ return to;
+ }
+ destroy() {
+ this.destroyed = true;
+ this.oHash.destroy();
+ this.iHash.destroy();
+ }
+ };
+ var hmac = (hash3, key, message) => new HMAC(hash3, key).update(message).digest();
+ hmac.create = (hash3, key) => new HMAC(hash3, key);
+
+ // node_modules/@noble/curves/esm/_shortw_utils.js
+ function getHash(hash3) {
+ return {
+ hash: hash3,
+ hmac: (key, ...msgs) => hmac(hash3, key, concatBytes(...msgs)),
+ randomBytes
+ };
+ }
+ function createCurve(curveDef, defHash) {
+ const create = (hash3) => weierstrass({ ...curveDef, ...getHash(hash3) });
+ return Object.freeze({ ...create(defHash), create });
+ }
+
+ // node_modules/@noble/curves/esm/secp256k1.js
+ var secp256k1P = BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
+ var secp256k1N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
+ var _1n5 = BigInt(1);
+ var _2n4 = BigInt(2);
+ var divNearest = (a, b) => (a + b / _2n4) / b;
+ function sqrtMod(y) {
+ const P = secp256k1P;
+ const _3n5 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
+ const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
+ const b2 = y * y * y % P;
+ const b3 = b2 * b2 * y % P;
+ const b6 = pow2(b3, _3n5, P) * b3 % P;
+ const b9 = pow2(b6, _3n5, P) * b3 % P;
+ const b11 = pow2(b9, _2n4, P) * b2 % P;
+ const b22 = pow2(b11, _11n, P) * b11 % P;
+ const b44 = pow2(b22, _22n, P) * b22 % P;
+ const b88 = pow2(b44, _44n, P) * b44 % P;
+ const b176 = pow2(b88, _88n, P) * b88 % P;
+ const b220 = pow2(b176, _44n, P) * b44 % P;
+ const b223 = pow2(b220, _3n5, P) * b3 % P;
+ const t1 = pow2(b223, _23n, P) * b22 % P;
+ const t2 = pow2(t1, _6n, P) * b2 % P;
+ const root = pow2(t2, _2n4, P);
+ if (!Fp.eql(Fp.sqr(root), y))
+ throw new Error("Cannot find square root");
+ return root;
+ }
+ var Fp = Field(secp256k1P, void 0, void 0, { sqrt: sqrtMod });
+ var secp256k1 = createCurve({
+ a: BigInt(0),
+ b: BigInt(7),
+ Fp,
+ n: secp256k1N,
+ Gx: BigInt("55066263022277343669578718895168534326250603453777594175500187360389116729240"),
+ Gy: BigInt("32670510020758816978083085130507043184471273380659243275938904335757337482424"),
+ h: BigInt(1),
+ lowS: true,
+ endo: {
+ beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"),
+ splitScalar: (k) => {
+ const n = secp256k1N;
+ const a1 = BigInt("0x3086d221a7d46bcde86c90e49284eb15");
+ const b1 = -_1n5 * BigInt("0xe4437ed6010e88286f547fa90abfe4c3");
+ const a2 = BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8");
+ const b2 = a1;
+ const POW_2_128 = BigInt("0x100000000000000000000000000000000");
+ const c1 = divNearest(b2 * k, n);
+ const c2 = divNearest(-b1 * k, n);
+ let k1 = mod(k - c1 * a1 - c2 * a2, n);
+ let k2 = mod(-c1 * b1 - c2 * b2, n);
+ const k1neg = k1 > POW_2_128;
+ const k2neg = k2 > POW_2_128;
+ if (k1neg)
+ k1 = n - k1;
+ if (k2neg)
+ k2 = n - k2;
+ if (k1 > POW_2_128 || k2 > POW_2_128) {
+ throw new Error("splitScalar: Endomorphism failed, k=" + k);
+ }
+ return { k1neg, k1, k2neg, k2 };
+ }
+ }
+ }, sha256);
+ var _0n5 = BigInt(0);
+ var fe = (x) => typeof x === "bigint" && _0n5 < x && x < secp256k1P;
+ var ge = (x) => typeof x === "bigint" && _0n5 < x && x < secp256k1N;
+ var TAGGED_HASH_PREFIXES = {};
+ function taggedHash(tag, ...messages) {
+ let tagP = TAGGED_HASH_PREFIXES[tag];
+ if (tagP === void 0) {
+ const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
+ tagP = concatBytes2(tagH, tagH);
+ TAGGED_HASH_PREFIXES[tag] = tagP;
+ }
+ return sha256(concatBytes2(tagP, ...messages));
+ }
+ var pointToBytes = (point) => point.toRawBytes(true).slice(1);
+ var numTo32b = (n) => numberToBytesBE(n, 32);
+ var modP = (x) => mod(x, secp256k1P);
+ var modN = (x) => mod(x, secp256k1N);
+ var Point = secp256k1.ProjectivePoint;
+ var GmulAdd = (Q, a, b) => Point.BASE.multiplyAndAddUnsafe(Q, a, b);
+ function schnorrGetExtPubKey(priv) {
+ let d_ = secp256k1.utils.normPrivateKeyToScalar(priv);
+ let p = Point.fromPrivateKey(d_);
+ const scalar = p.hasEvenY() ? d_ : modN(-d_);
+ return { scalar, bytes: pointToBytes(p) };
+ }
+ function lift_x(x) {
+ if (!fe(x))
+ throw new Error("bad x: need 0 < x < p");
+ const xx = modP(x * x);
+ const c = modP(xx * x + BigInt(7));
+ let y = sqrtMod(c);
+ if (y % _2n4 !== _0n5)
+ y = modP(-y);
+ const p = new Point(x, y, _1n5);
+ p.assertValidity();
+ return p;
+ }
+ function challenge(...args) {
+ return modN(bytesToNumberBE(taggedHash("BIP0340/challenge", ...args)));
+ }
+ function schnorrGetPublicKey(privateKey) {
+ return schnorrGetExtPubKey(privateKey).bytes;
+ }
+ function schnorrSign(message, privateKey, auxRand = randomBytes(32)) {
+ const m = ensureBytes("message", message);
+ const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey);
+ const a = ensureBytes("auxRand", auxRand, 32);
+ const t = numTo32b(d ^ bytesToNumberBE(taggedHash("BIP0340/aux", a)));
+ const rand = taggedHash("BIP0340/nonce", t, px, m);
+ const k_ = modN(bytesToNumberBE(rand));
+ if (k_ === _0n5)
+ throw new Error("sign failed: k is zero");
+ const { bytes: rx, scalar: k } = schnorrGetExtPubKey(k_);
+ const e = challenge(rx, px, m);
+ const sig = new Uint8Array(64);
+ sig.set(rx, 0);
+ sig.set(numTo32b(modN(k + e * d)), 32);
+ if (!schnorrVerify(sig, m, px))
+ throw new Error("sign: Invalid signature produced");
+ return sig;
+ }
+ function schnorrVerify(signature, message, publicKey) {
+ const sig = ensureBytes("signature", signature, 64);
+ const m = ensureBytes("message", message);
+ const pub = ensureBytes("publicKey", publicKey, 32);
+ try {
+ const P = lift_x(bytesToNumberBE(pub));
+ const r = bytesToNumberBE(sig.subarray(0, 32));
+ if (!fe(r))
+ return false;
+ const s = bytesToNumberBE(sig.subarray(32, 64));
+ if (!ge(s))
+ return false;
+ const e = challenge(numTo32b(r), pointToBytes(P), m);
+ const R = GmulAdd(P, s, modN(-e));
+ if (!R || !R.hasEvenY() || R.toAffine().x !== r)
+ return false;
+ return true;
+ } catch (error) {
+ return false;
+ }
+ }
+ var schnorr = /* @__PURE__ */ (() => ({
+ getPublicKey: schnorrGetPublicKey,
+ sign: schnorrSign,
+ verify: schnorrVerify,
+ utils: {
+ randomPrivateKey: secp256k1.utils.randomPrivateKey,
+ lift_x,
+ pointToBytes,
+ numberToBytesBE,
+ bytesToNumberBE,
+ taggedHash,
+ mod
+ }
+ }))();
+
+ // node_modules/@noble/hashes/esm/crypto.js
+ var crypto2 = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0;
+
+ // node_modules/@noble/hashes/esm/utils.js
+ var u8a3 = (a) => a instanceof Uint8Array;
+ var createView2 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
+ var rotr2 = (word, shift) => word << 32 - shift | word >>> shift;
+ var isLE2 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
+ if (!isLE2)
+ throw new Error("Non little-endian hardware is not supported");
+ var hexes2 = Array.from({ length: 256 }, (v, i2) => i2.toString(16).padStart(2, "0"));
+ function bytesToHex2(bytes4) {
+ if (!u8a3(bytes4))
+ throw new Error("Uint8Array expected");
+ let hex2 = "";
+ for (let i2 = 0; i2 < bytes4.length; i2++) {
+ hex2 += hexes2[bytes4[i2]];
+ }
+ return hex2;
+ }
+ function hexToBytes2(hex2) {
+ if (typeof hex2 !== "string")
+ throw new Error("hex string expected, got " + typeof hex2);
+ const len = hex2.length;
+ if (len % 2)
+ throw new Error("padded hex string expected, got unpadded hex of length " + len);
+ const array = new Uint8Array(len / 2);
+ for (let i2 = 0; i2 < array.length; i2++) {
+ const j = i2 * 2;
+ const hexByte = hex2.slice(j, j + 2);
+ const byte = Number.parseInt(hexByte, 16);
+ if (Number.isNaN(byte) || byte < 0)
+ throw new Error("Invalid byte sequence");
+ array[i2] = byte;
+ }
+ return array;
+ }
+ function utf8ToBytes3(str) {
+ if (typeof str !== "string")
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
+ return new Uint8Array(new TextEncoder().encode(str));
+ }
+ function toBytes2(data) {
+ if (typeof data === "string")
+ data = utf8ToBytes3(data);
+ if (!u8a3(data))
+ throw new Error(`expected Uint8Array, got ${typeof data}`);
+ return data;
+ }
+ function concatBytes3(...arrays) {
+ const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
+ let pad2 = 0;
+ arrays.forEach((a) => {
+ if (!u8a3(a))
+ throw new Error("Uint8Array expected");
+ r.set(a, pad2);
+ pad2 += a.length;
+ });
+ return r;
+ }
+ var Hash2 = class {
+ clone() {
+ return this._cloneInto();
+ }
+ };
+ var isPlainObject = (obj) => Object.prototype.toString.call(obj) === "[object Object]" && obj.constructor === Object;
+ function checkOpts(defaults, opts) {
+ if (opts !== void 0 && (typeof opts !== "object" || !isPlainObject(opts)))
+ throw new Error("Options should be object or undefined");
+ const merged = Object.assign(defaults, opts);
+ return merged;
+ }
+ function wrapConstructor2(hashCons) {
+ const hashC = (msg) => hashCons().update(toBytes2(msg)).digest();
+ const tmp = hashCons();
+ hashC.outputLen = tmp.outputLen;
+ hashC.blockLen = tmp.blockLen;
+ hashC.create = () => hashCons();
+ return hashC;
+ }
+ function randomBytes2(bytesLength = 32) {
+ if (crypto2 && typeof crypto2.getRandomValues === "function") {
+ return crypto2.getRandomValues(new Uint8Array(bytesLength));
+ }
+ throw new Error("crypto.getRandomValues must be defined");
+ }
+
+ // core.ts
+ var verifiedSymbol = Symbol("verified");
+ var isRecord = (obj) => obj instanceof Object;
+ function validateEvent(event) {
+ if (!isRecord(event))
+ return false;
+ if (typeof event.kind !== "number")
+ return false;
+ if (typeof event.content !== "string")
+ return false;
+ if (typeof event.created_at !== "number")
+ return false;
+ if (typeof event.pubkey !== "string")
+ return false;
+ if (!event.pubkey.match(/^[a-f0-9]{64}$/))
+ return false;
+ if (!Array.isArray(event.tags))
+ return false;
+ for (let i2 = 0; i2 < event.tags.length; i2++) {
+ let tag = event.tags[i2];
+ if (!Array.isArray(tag))
+ return false;
+ for (let j = 0; j < tag.length; j++) {
+ if (typeof tag[j] !== "string")
+ return false;
+ }
+ }
+ return true;
+ }
+ function sortEvents(events) {
+ return events.sort((a, b) => {
+ if (a.created_at !== b.created_at) {
+ return b.created_at - a.created_at;
+ }
+ return a.id.localeCompare(b.id);
+ });
+ }
+
+ // node_modules/@noble/hashes/esm/_assert.js
+ function number2(n) {
+ if (!Number.isSafeInteger(n) || n < 0)
+ throw new Error(`Wrong positive integer: ${n}`);
+ }
+ function bool(b) {
+ if (typeof b !== "boolean")
+ throw new Error(`Expected boolean, not ${b}`);
+ }
+ function bytes2(b, ...lengths) {
+ if (!(b instanceof Uint8Array))
+ throw new Error("Expected Uint8Array");
+ if (lengths.length > 0 && !lengths.includes(b.length))
+ throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
+ }
+ function hash2(hash3) {
+ if (typeof hash3 !== "function" || typeof hash3.create !== "function")
+ throw new Error("Hash should be wrapped by utils.wrapConstructor");
+ number2(hash3.outputLen);
+ number2(hash3.blockLen);
+ }
+ function exists2(instance, checkFinished = true) {
+ if (instance.destroyed)
+ throw new Error("Hash instance has been destroyed");
+ if (checkFinished && instance.finished)
+ throw new Error("Hash#digest() has already been called");
+ }
+ function output2(out, instance) {
+ bytes2(out);
+ const min = instance.outputLen;
+ if (out.length < min) {
+ throw new Error(`digestInto() expects output buffer of length at least ${min}`);
+ }
+ }
+ var assert = {
+ number: number2,
+ bool,
+ bytes: bytes2,
+ hash: hash2,
+ exists: exists2,
+ output: output2
+ };
+ var assert_default = assert;
+
+ // node_modules/@noble/hashes/esm/_sha2.js
+ function setBigUint642(view, byteOffset, value, isLE4) {
+ if (typeof view.setBigUint64 === "function")
+ return view.setBigUint64(byteOffset, value, isLE4);
+ const _32n2 = BigInt(32);
+ const _u32_max = BigInt(4294967295);
+ const wh = Number(value >> _32n2 & _u32_max);
+ const wl = Number(value & _u32_max);
+ const h = isLE4 ? 4 : 0;
+ const l = isLE4 ? 0 : 4;
+ view.setUint32(byteOffset + h, wh, isLE4);
+ view.setUint32(byteOffset + l, wl, isLE4);
+ }
+ var SHA22 = class extends Hash2 {
+ constructor(blockLen, outputLen, padOffset, isLE4) {
+ super();
+ this.blockLen = blockLen;
+ this.outputLen = outputLen;
+ this.padOffset = padOffset;
+ this.isLE = isLE4;
+ this.finished = false;
+ this.length = 0;
+ this.pos = 0;
+ this.destroyed = false;
+ this.buffer = new Uint8Array(blockLen);
+ this.view = createView2(this.buffer);
+ }
+ update(data) {
+ assert_default.exists(this);
+ const { view, buffer, blockLen } = this;
+ data = toBytes2(data);
+ const len = data.length;
+ for (let pos = 0; pos < len; ) {
+ const take = Math.min(blockLen - this.pos, len - pos);
+ if (take === blockLen) {
+ const dataView = createView2(data);
+ for (; blockLen <= len - pos; pos += blockLen)
+ this.process(dataView, pos);
+ continue;
+ }
+ buffer.set(data.subarray(pos, pos + take), this.pos);
+ this.pos += take;
+ pos += take;
+ if (this.pos === blockLen) {
+ this.process(view, 0);
+ this.pos = 0;
+ }
+ }
+ this.length += data.length;
+ this.roundClean();
+ return this;
+ }
+ digestInto(out) {
+ assert_default.exists(this);
+ assert_default.output(out, this);
+ this.finished = true;
+ const { buffer, view, blockLen, isLE: isLE4 } = this;
+ let { pos } = this;
+ buffer[pos++] = 128;
+ this.buffer.subarray(pos).fill(0);
+ if (this.padOffset > blockLen - pos) {
+ this.process(view, 0);
+ pos = 0;
+ }
+ for (let i2 = pos; i2 < blockLen; i2++)
+ buffer[i2] = 0;
+ setBigUint642(view, blockLen - 8, BigInt(this.length * 8), isLE4);
+ this.process(view, 0);
+ const oview = createView2(out);
+ const len = this.outputLen;
+ if (len % 4)
+ throw new Error("_sha2: outputLen should be aligned to 32bit");
+ const outLen = len / 4;
+ const state = this.get();
+ if (outLen > state.length)
+ throw new Error("_sha2: outputLen bigger than state");
+ for (let i2 = 0; i2 < outLen; i2++)
+ oview.setUint32(4 * i2, state[i2], isLE4);
+ }
+ digest() {
+ const { buffer, outputLen } = this;
+ this.digestInto(buffer);
+ const res = buffer.slice(0, outputLen);
+ this.destroy();
+ return res;
+ }
+ _cloneInto(to) {
+ to || (to = new this.constructor());
+ to.set(...this.get());
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
+ to.length = length;
+ to.pos = pos;
+ to.finished = finished;
+ to.destroyed = destroyed;
+ if (length % blockLen)
+ to.buffer.set(buffer);
+ return to;
+ }
+ };
+
+ // node_modules/@noble/hashes/esm/sha256.js
+ var Chi2 = (a, b, c) => a & b ^ ~a & c;
+ var Maj2 = (a, b, c) => a & b ^ a & c ^ b & c;
+ var SHA256_K2 = new Uint32Array([
+ 1116352408,
+ 1899447441,
+ 3049323471,
+ 3921009573,
+ 961987163,
+ 1508970993,
+ 2453635748,
+ 2870763221,
+ 3624381080,
+ 310598401,
+ 607225278,
+ 1426881987,
+ 1925078388,
+ 2162078206,
+ 2614888103,
+ 3248222580,
+ 3835390401,
+ 4022224774,
+ 264347078,
+ 604807628,
+ 770255983,
+ 1249150122,
+ 1555081692,
+ 1996064986,
+ 2554220882,
+ 2821834349,
+ 2952996808,
+ 3210313671,
+ 3336571891,
+ 3584528711,
+ 113926993,
+ 338241895,
+ 666307205,
+ 773529912,
+ 1294757372,
+ 1396182291,
+ 1695183700,
+ 1986661051,
+ 2177026350,
+ 2456956037,
+ 2730485921,
+ 2820302411,
+ 3259730800,
+ 3345764771,
+ 3516065817,
+ 3600352804,
+ 4094571909,
+ 275423344,
+ 430227734,
+ 506948616,
+ 659060556,
+ 883997877,
+ 958139571,
+ 1322822218,
+ 1537002063,
+ 1747873779,
+ 1955562222,
+ 2024104815,
+ 2227730452,
+ 2361852424,
+ 2428436474,
+ 2756734187,
+ 3204031479,
+ 3329325298
+ ]);
+ var IV2 = new Uint32Array([
+ 1779033703,
+ 3144134277,
+ 1013904242,
+ 2773480762,
+ 1359893119,
+ 2600822924,
+ 528734635,
+ 1541459225
+ ]);
+ var SHA256_W2 = new Uint32Array(64);
+ var SHA2562 = class extends SHA22 {
+ constructor() {
+ super(64, 32, 8, false);
+ this.A = IV2[0] | 0;
+ this.B = IV2[1] | 0;
+ this.C = IV2[2] | 0;
+ this.D = IV2[3] | 0;
+ this.E = IV2[4] | 0;
+ this.F = IV2[5] | 0;
+ this.G = IV2[6] | 0;
+ this.H = IV2[7] | 0;
+ }
+ get() {
+ const { A, B, C, D, E, F, G, H } = this;
+ return [A, B, C, D, E, F, G, H];
+ }
+ set(A, B, C, D, E, F, G, H) {
+ this.A = A | 0;
+ this.B = B | 0;
+ this.C = C | 0;
+ this.D = D | 0;
+ this.E = E | 0;
+ this.F = F | 0;
+ this.G = G | 0;
+ this.H = H | 0;
+ }
+ process(view, offset) {
+ for (let i2 = 0; i2 < 16; i2++, offset += 4)
+ SHA256_W2[i2] = view.getUint32(offset, false);
+ for (let i2 = 16; i2 < 64; i2++) {
+ const W15 = SHA256_W2[i2 - 15];
+ const W2 = SHA256_W2[i2 - 2];
+ const s0 = rotr2(W15, 7) ^ rotr2(W15, 18) ^ W15 >>> 3;
+ const s1 = rotr2(W2, 17) ^ rotr2(W2, 19) ^ W2 >>> 10;
+ SHA256_W2[i2] = s1 + SHA256_W2[i2 - 7] + s0 + SHA256_W2[i2 - 16] | 0;
+ }
+ let { A, B, C, D, E, F, G, H } = this;
+ for (let i2 = 0; i2 < 64; i2++) {
+ const sigma1 = rotr2(E, 6) ^ rotr2(E, 11) ^ rotr2(E, 25);
+ const T1 = H + sigma1 + Chi2(E, F, G) + SHA256_K2[i2] + SHA256_W2[i2] | 0;
+ const sigma0 = rotr2(A, 2) ^ rotr2(A, 13) ^ rotr2(A, 22);
+ const T2 = sigma0 + Maj2(A, B, C) | 0;
+ H = G;
+ G = F;
+ F = E;
+ E = D + T1 | 0;
+ D = C;
+ C = B;
+ B = A;
+ A = T1 + T2 | 0;
+ }
+ A = A + this.A | 0;
+ B = B + this.B | 0;
+ C = C + this.C | 0;
+ D = D + this.D | 0;
+ E = E + this.E | 0;
+ F = F + this.F | 0;
+ G = G + this.G | 0;
+ H = H + this.H | 0;
+ this.set(A, B, C, D, E, F, G, H);
+ }
+ roundClean() {
+ SHA256_W2.fill(0);
+ }
+ destroy() {
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
+ this.buffer.fill(0);
+ }
+ };
+ var SHA224 = class extends SHA2562 {
+ constructor() {
+ super();
+ this.A = 3238371032 | 0;
+ this.B = 914150663 | 0;
+ this.C = 812702999 | 0;
+ this.D = 4144912697 | 0;
+ this.E = 4290775857 | 0;
+ this.F = 1750603025 | 0;
+ this.G = 1694076839 | 0;
+ this.H = 3204075428 | 0;
+ this.outputLen = 28;
+ }
+ };
+ var sha2562 = wrapConstructor2(() => new SHA2562());
+ var sha224 = wrapConstructor2(() => new SHA224());
+
+ // utils.ts
+ var utils_exports2 = {};
+ __export(utils_exports2, {
+ Queue: () => Queue,
+ QueueNode: () => QueueNode,
+ binarySearch: () => binarySearch,
+ bytesToHex: () => bytesToHex2,
+ hexToBytes: () => hexToBytes2,
+ insertEventIntoAscendingList: () => insertEventIntoAscendingList,
+ insertEventIntoDescendingList: () => insertEventIntoDescendingList,
+ normalizeURL: () => normalizeURL,
+ utf8Decoder: () => utf8Decoder,
+ utf8Encoder: () => utf8Encoder
+ });
+ var utf8Decoder = new TextDecoder("utf-8");
+ var utf8Encoder = new TextEncoder();
+ function normalizeURL(url) {
+ try {
+ if (url.indexOf("://") === -1)
+ url = "wss://" + url;
+ let p = new URL(url);
+ p.pathname = p.pathname.replace(/\/+/g, "/");
+ if (p.pathname.endsWith("/"))
+ p.pathname = p.pathname.slice(0, -1);
+ if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
+ p.port = "";
+ p.searchParams.sort();
+ p.hash = "";
+ return p.toString();
+ } catch (e) {
+ throw new Error(`Invalid URL: ${url}`);
+ }
+ }
+ function insertEventIntoDescendingList(sortedArray, event) {
+ const [idx, found] = binarySearch(sortedArray, (b) => {
+ if (event.id === b.id)
+ return 0;
+ if (event.created_at === b.created_at)
+ return -1;
+ return b.created_at - event.created_at;
+ });
+ if (!found) {
+ sortedArray.splice(idx, 0, event);
+ }
+ return sortedArray;
+ }
+ function insertEventIntoAscendingList(sortedArray, event) {
+ const [idx, found] = binarySearch(sortedArray, (b) => {
+ if (event.id === b.id)
+ return 0;
+ if (event.created_at === b.created_at)
+ return -1;
+ return event.created_at - b.created_at;
+ });
+ if (!found) {
+ sortedArray.splice(idx, 0, event);
+ }
+ return sortedArray;
+ }
+ function binarySearch(arr, compare) {
+ let start = 0;
+ let end = arr.length - 1;
+ while (start <= end) {
+ const mid = Math.floor((start + end) / 2);
+ const cmp = compare(arr[mid]);
+ if (cmp === 0) {
+ return [mid, true];
+ }
+ if (cmp < 0) {
+ end = mid - 1;
+ } else {
+ start = mid + 1;
+ }
+ }
+ return [start, false];
+ }
+ var QueueNode = class {
+ value;
+ next = null;
+ prev = null;
+ constructor(message) {
+ this.value = message;
+ }
+ };
+ var Queue = class {
+ first;
+ last;
+ constructor() {
+ this.first = null;
+ this.last = null;
+ }
+ enqueue(value) {
+ const newNode = new QueueNode(value);
+ if (!this.last) {
+ this.first = newNode;
+ this.last = newNode;
+ } else if (this.last === this.first) {
+ this.last = newNode;
+ this.last.prev = this.first;
+ this.first.next = newNode;
+ } else {
+ newNode.prev = this.last;
+ this.last.next = newNode;
+ this.last = newNode;
+ }
+ return true;
+ }
+ dequeue() {
+ if (!this.first)
+ return null;
+ if (this.first === this.last) {
+ const target2 = this.first;
+ this.first = null;
+ this.last = null;
+ return target2.value;
+ }
+ const target = this.first;
+ this.first = target.next;
+ if (this.first) {
+ this.first.prev = null;
+ }
+ return target.value;
+ }
+ };
+
+ // pure.ts
+ var JS = class {
+ generateSecretKey() {
+ return schnorr.utils.randomPrivateKey();
+ }
+ getPublicKey(secretKey) {
+ return bytesToHex2(schnorr.getPublicKey(secretKey));
+ }
+ finalizeEvent(t, secretKey) {
+ const event = t;
+ event.pubkey = bytesToHex2(schnorr.getPublicKey(secretKey));
+ event.id = getEventHash(event);
+ event.sig = bytesToHex2(schnorr.sign(getEventHash(event), secretKey));
+ event[verifiedSymbol] = true;
+ return event;
+ }
+ verifyEvent(event) {
+ if (typeof event[verifiedSymbol] === "boolean")
+ return event[verifiedSymbol];
+ const hash3 = getEventHash(event);
+ if (hash3 !== event.id) {
+ event[verifiedSymbol] = false;
+ return false;
+ }
+ try {
+ const valid = schnorr.verify(event.sig, hash3, event.pubkey);
+ event[verifiedSymbol] = valid;
+ return valid;
+ } catch (err) {
+ event[verifiedSymbol] = false;
+ return false;
+ }
+ }
+ };
+ function serializeEvent(evt) {
+ if (!validateEvent(evt))
+ throw new Error("can't serialize event with wrong or missing properties");
+ return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
+ }
+ function getEventHash(event) {
+ let eventHash = sha2562(utf8Encoder.encode(serializeEvent(event)));
+ return bytesToHex2(eventHash);
+ }
+ var i = new JS();
+ var generateSecretKey = i.generateSecretKey;
+ var getPublicKey = i.getPublicKey;
+ var finalizeEvent = i.finalizeEvent;
+ var verifyEvent = i.verifyEvent;
+
+ // kinds.ts
+ var kinds_exports = {};
+ __export(kinds_exports, {
+ Application: () => Application,
+ BadgeAward: () => BadgeAward,
+ BadgeDefinition: () => BadgeDefinition,
+ BlockedRelaysList: () => BlockedRelaysList,
+ BookmarkList: () => BookmarkList,
+ Bookmarksets: () => Bookmarksets,
+ Calendar: () => Calendar,
+ CalendarEventRSVP: () => CalendarEventRSVP,
+ ChannelCreation: () => ChannelCreation,
+ ChannelHideMessage: () => ChannelHideMessage,
+ ChannelMessage: () => ChannelMessage,
+ ChannelMetadata: () => ChannelMetadata,
+ ChannelMuteUser: () => ChannelMuteUser,
+ ClassifiedListing: () => ClassifiedListing,
+ ClientAuth: () => ClientAuth,
+ CommunitiesList: () => CommunitiesList,
+ CommunityDefinition: () => CommunityDefinition,
+ CommunityPostApproval: () => CommunityPostApproval,
+ Contacts: () => Contacts,
+ CreateOrUpdateProduct: () => CreateOrUpdateProduct,
+ CreateOrUpdateStall: () => CreateOrUpdateStall,
+ Curationsets: () => Curationsets,
+ Date: () => Date2,
+ DirectMessageRelaysList: () => DirectMessageRelaysList,
+ DraftClassifiedListing: () => DraftClassifiedListing,
+ DraftLong: () => DraftLong,
+ Emojisets: () => Emojisets,
+ EncryptedDirectMessage: () => EncryptedDirectMessage,
+ EventDeletion: () => EventDeletion,
+ FileMetadata: () => FileMetadata,
+ FileServerPreference: () => FileServerPreference,
+ Followsets: () => Followsets,
+ GenericRepost: () => GenericRepost,
+ Genericlists: () => Genericlists,
+ GiftWrap: () => GiftWrap,
+ HTTPAuth: () => HTTPAuth,
+ Handlerinformation: () => Handlerinformation,
+ Handlerrecommendation: () => Handlerrecommendation,
+ Highlights: () => Highlights,
+ InterestsList: () => InterestsList,
+ Interestsets: () => Interestsets,
+ JobFeedback: () => JobFeedback,
+ JobRequest: () => JobRequest,
+ JobResult: () => JobResult,
+ Label: () => Label,
+ LightningPubRPC: () => LightningPubRPC,
+ LiveChatMessage: () => LiveChatMessage,
+ LiveEvent: () => LiveEvent,
+ LongFormArticle: () => LongFormArticle,
+ Metadata: () => Metadata,
+ Mutelist: () => Mutelist,
+ NWCWalletInfo: () => NWCWalletInfo,
+ NWCWalletRequest: () => NWCWalletRequest,
+ NWCWalletResponse: () => NWCWalletResponse,
+ NostrConnect: () => NostrConnect,
+ OpenTimestamps: () => OpenTimestamps,
+ Pinlist: () => Pinlist,
+ PrivateDirectMessage: () => PrivateDirectMessage,
+ ProblemTracker: () => ProblemTracker,
+ ProfileBadges: () => ProfileBadges,
+ PublicChatsList: () => PublicChatsList,
+ Reaction: () => Reaction,
+ RecommendRelay: () => RecommendRelay,
+ RelayList: () => RelayList,
+ Relaysets: () => Relaysets,
+ Report: () => Report,
+ Reporting: () => Reporting,
+ Repost: () => Repost,
+ Seal: () => Seal,
+ SearchRelaysList: () => SearchRelaysList,
+ ShortTextNote: () => ShortTextNote,
+ Time: () => Time,
+ UserEmojiList: () => UserEmojiList,
+ UserStatuses: () => UserStatuses,
+ Zap: () => Zap,
+ ZapGoal: () => ZapGoal,
+ ZapRequest: () => ZapRequest,
+ classifyKind: () => classifyKind,
+ isAddressableKind: () => isAddressableKind,
+ isEphemeralKind: () => isEphemeralKind,
+ isKind: () => isKind,
+ isRegularKind: () => isRegularKind,
+ isReplaceableKind: () => isReplaceableKind
+ });
+ function isRegularKind(kind) {
+ return 1e3 <= kind && kind < 1e4 || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind);
+ }
+ function isReplaceableKind(kind) {
+ return [0, 3].includes(kind) || 1e4 <= kind && kind < 2e4;
+ }
+ function isEphemeralKind(kind) {
+ return 2e4 <= kind && kind < 3e4;
+ }
+ function isAddressableKind(kind) {
+ return 3e4 <= kind && kind < 4e4;
+ }
+ function classifyKind(kind) {
+ if (isRegularKind(kind))
+ return "regular";
+ if (isReplaceableKind(kind))
+ return "replaceable";
+ if (isEphemeralKind(kind))
+ return "ephemeral";
+ if (isAddressableKind(kind))
+ return "parameterized";
+ return "unknown";
+ }
+ function isKind(event, kind) {
+ const kindAsArray = kind instanceof Array ? kind : [kind];
+ return validateEvent(event) && kindAsArray.includes(event.kind) || false;
+ }
+ var Metadata = 0;
+ var ShortTextNote = 1;
+ var RecommendRelay = 2;
+ var Contacts = 3;
+ var EncryptedDirectMessage = 4;
+ var EventDeletion = 5;
+ var Repost = 6;
+ var Reaction = 7;
+ var BadgeAward = 8;
+ var Seal = 13;
+ var PrivateDirectMessage = 14;
+ var GenericRepost = 16;
+ var ChannelCreation = 40;
+ var ChannelMetadata = 41;
+ var ChannelMessage = 42;
+ var ChannelHideMessage = 43;
+ var ChannelMuteUser = 44;
+ var OpenTimestamps = 1040;
+ var GiftWrap = 1059;
+ var FileMetadata = 1063;
+ var LiveChatMessage = 1311;
+ var ProblemTracker = 1971;
+ var Report = 1984;
+ var Reporting = 1984;
+ var Label = 1985;
+ var CommunityPostApproval = 4550;
+ var JobRequest = 5999;
+ var JobResult = 6999;
+ var JobFeedback = 7e3;
+ var ZapGoal = 9041;
+ var ZapRequest = 9734;
+ var Zap = 9735;
+ var Highlights = 9802;
+ var Mutelist = 1e4;
+ var Pinlist = 10001;
+ var RelayList = 10002;
+ var BookmarkList = 10003;
+ var CommunitiesList = 10004;
+ var PublicChatsList = 10005;
+ var BlockedRelaysList = 10006;
+ var SearchRelaysList = 10007;
+ var InterestsList = 10015;
+ var UserEmojiList = 10030;
+ var DirectMessageRelaysList = 10050;
+ var FileServerPreference = 10096;
+ var NWCWalletInfo = 13194;
+ var LightningPubRPC = 21e3;
+ var ClientAuth = 22242;
+ var NWCWalletRequest = 23194;
+ var NWCWalletResponse = 23195;
+ var NostrConnect = 24133;
+ var HTTPAuth = 27235;
+ var Followsets = 3e4;
+ var Genericlists = 30001;
+ var Relaysets = 30002;
+ var Bookmarksets = 30003;
+ var Curationsets = 30004;
+ var ProfileBadges = 30008;
+ var BadgeDefinition = 30009;
+ var Interestsets = 30015;
+ var CreateOrUpdateStall = 30017;
+ var CreateOrUpdateProduct = 30018;
+ var LongFormArticle = 30023;
+ var DraftLong = 30024;
+ var Emojisets = 30030;
+ var Application = 30078;
+ var LiveEvent = 30311;
+ var UserStatuses = 30315;
+ var ClassifiedListing = 30402;
+ var DraftClassifiedListing = 30403;
+ var Date2 = 31922;
+ var Time = 31923;
+ var Calendar = 31924;
+ var CalendarEventRSVP = 31925;
+ var Handlerrecommendation = 31989;
+ var Handlerinformation = 31990;
+ var CommunityDefinition = 34550;
+
+ // filter.ts
+ function matchFilter(filter, event) {
+ if (filter.ids && filter.ids.indexOf(event.id) === -1) {
+ return false;
+ }
+ if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
+ return false;
+ }
+ if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
+ return false;
+ }
+ for (let f2 in filter) {
+ if (f2[0] === "#") {
+ let tagName = f2.slice(1);
+ let values = filter[`#${tagName}`];
+ if (values && !event.tags.find(([t, v]) => t === f2.slice(1) && values.indexOf(v) !== -1))
+ return false;
+ }
+ }
+ if (filter.since && event.created_at < filter.since)
+ return false;
+ if (filter.until && event.created_at > filter.until)
+ return false;
+ return true;
+ }
+ function matchFilters(filters, event) {
+ for (let i2 = 0; i2 < filters.length; i2++) {
+ if (matchFilter(filters[i2], event)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ function mergeFilters(...filters) {
+ let result = {};
+ for (let i2 = 0; i2 < filters.length; i2++) {
+ let filter = filters[i2];
+ Object.entries(filter).forEach(([property, values]) => {
+ if (property === "kinds" || property === "ids" || property === "authors" || property[0] === "#") {
+ result[property] = result[property] || [];
+ for (let v = 0; v < values.length; v++) {
+ let value = values[v];
+ if (!result[property].includes(value))
+ result[property].push(value);
+ }
+ }
+ });
+ if (filter.limit && (!result.limit || filter.limit > result.limit))
+ result.limit = filter.limit;
+ if (filter.until && (!result.until || filter.until > result.until))
+ result.until = filter.until;
+ if (filter.since && (!result.since || filter.since < result.since))
+ result.since = filter.since;
+ }
+ return result;
+ }
+ function getFilterLimit(filter) {
+ if (filter.ids && !filter.ids.length)
+ return 0;
+ if (filter.kinds && !filter.kinds.length)
+ return 0;
+ if (filter.authors && !filter.authors.length)
+ return 0;
+ for (const [key, value] of Object.entries(filter)) {
+ if (key[0] === "#" && Array.isArray(value) && !value.length)
+ return 0;
+ }
+ return Math.min(
+ Math.max(0, filter.limit ?? Infinity),
+ filter.ids?.length ?? Infinity,
+ filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
+ filter.authors?.length && filter.kinds?.every((kind) => isAddressableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
+ );
+ }
+
+ // fakejson.ts
+ var fakejson_exports = {};
+ __export(fakejson_exports, {
+ getHex64: () => getHex64,
+ getInt: () => getInt,
+ getSubscriptionId: () => getSubscriptionId,
+ matchEventId: () => matchEventId,
+ matchEventKind: () => matchEventKind,
+ matchEventPubkey: () => matchEventPubkey
+ });
+ function getHex64(json, field) {
+ let len = field.length + 3;
+ let idx = json.indexOf(`"${field}":`) + len;
+ let s = json.slice(idx).indexOf(`"`) + idx + 1;
+ return json.slice(s, s + 64);
+ }
+ function getInt(json, field) {
+ let len = field.length;
+ let idx = json.indexOf(`"${field}":`) + len + 3;
+ let sliced = json.slice(idx);
+ let end = Math.min(sliced.indexOf(","), sliced.indexOf("}"));
+ return parseInt(sliced.slice(0, end), 10);
+ }
+ function getSubscriptionId(json) {
+ let idx = json.slice(0, 22).indexOf(`"EVENT"`);
+ if (idx === -1)
+ return null;
+ let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
+ if (pstart === -1)
+ return null;
+ let start = idx + 7 + 1 + pstart;
+ let pend = json.slice(start + 1, 80).indexOf(`"`);
+ if (pend === -1)
+ return null;
+ let end = start + 1 + pend;
+ return json.slice(start + 1, end);
+ }
+ function matchEventId(json, id) {
+ return id === getHex64(json, "id");
+ }
+ function matchEventPubkey(json, pubkey) {
+ return pubkey === getHex64(json, "pubkey");
+ }
+ function matchEventKind(json, kind) {
+ return kind === getInt(json, "kind");
+ }
+
+ // nip42.ts
+ var nip42_exports = {};
+ __export(nip42_exports, {
+ makeAuthEvent: () => makeAuthEvent
+ });
+ function makeAuthEvent(relayURL, challenge2) {
+ return {
+ kind: ClientAuth,
+ created_at: Math.floor(Date.now() / 1e3),
+ tags: [
+ ["relay", relayURL],
+ ["challenge", challenge2]
+ ],
+ content: ""
+ };
+ }
+
+ // helpers.ts
+ async function yieldThread() {
+ return new Promise((resolve) => {
+ const ch = new MessageChannel();
+ const handler = () => {
+ ch.port1.removeEventListener("message", handler);
+ resolve();
+ };
+ ch.port1.addEventListener("message", handler);
+ ch.port2.postMessage(0);
+ ch.port1.start();
+ });
+ }
+ var alwaysTrue = (t) => {
+ t[verifiedSymbol] = true;
+ return true;
+ };
+
+ // abstract-relay.ts
+ var SendingOnClosedConnection = class extends Error {
+ constructor(message, relay) {
+ super(`Tried to send message '${message} on a closed connection to ${relay}.`);
+ this.name = "SendingOnClosedConnection";
+ }
+ };
+ var AbstractRelay = class {
+ url;
+ _connected = false;
+ onclose = null;
+ onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
+ baseEoseTimeout = 4400;
+ connectionTimeout = 4400;
+ publishTimeout = 4400;
+ pingFrequency = 2e4;
+ pingTimeout = 2e4;
+ openSubs = /* @__PURE__ */ new Map();
+ enablePing;
+ connectionTimeoutHandle;
+ connectionPromise;
+ openCountRequests = /* @__PURE__ */ new Map();
+ openEventPublishes = /* @__PURE__ */ new Map();
+ ws;
+ incomingMessageQueue = new Queue();
+ queueRunning = false;
+ challenge;
+ authPromise;
+ serial = 0;
+ verifyEvent;
+ _WebSocket;
+ constructor(url, opts) {
+ this.url = normalizeURL(url);
+ this.verifyEvent = opts.verifyEvent;
+ this._WebSocket = opts.websocketImplementation || WebSocket;
+ this.enablePing = opts.enablePing;
+ }
+ static async connect(url, opts) {
+ const relay = new AbstractRelay(url, opts);
+ await relay.connect();
+ return relay;
+ }
+ closeAllSubscriptions(reason) {
+ for (let [_, sub] of this.openSubs) {
+ sub.close(reason);
+ }
+ this.openSubs.clear();
+ for (let [_, ep] of this.openEventPublishes) {
+ ep.reject(new Error(reason));
+ }
+ this.openEventPublishes.clear();
+ for (let [_, cr] of this.openCountRequests) {
+ cr.reject(new Error(reason));
+ }
+ this.openCountRequests.clear();
+ }
+ get connected() {
+ return this._connected;
+ }
+ async connect() {
+ if (this.connectionPromise)
+ return this.connectionPromise;
+ this.challenge = void 0;
+ this.authPromise = void 0;
+ this.connectionPromise = new Promise((resolve, reject) => {
+ this.connectionTimeoutHandle = setTimeout(() => {
+ reject("connection timed out");
+ this.connectionPromise = void 0;
+ this.onclose?.();
+ this.closeAllSubscriptions("relay connection timed out");
+ }, this.connectionTimeout);
+ try {
+ this.ws = new this._WebSocket(this.url);
+ } catch (err) {
+ clearTimeout(this.connectionTimeoutHandle);
+ reject(err);
+ return;
+ }
+ this.ws.onopen = () => {
+ clearTimeout(this.connectionTimeoutHandle);
+ this._connected = true;
+ if (this.enablePing) {
+ this.pingpong();
+ }
+ resolve();
+ };
+ this.ws.onerror = (ev) => {
+ clearTimeout(this.connectionTimeoutHandle);
+ reject(ev.message || "websocket error");
+ this._connected = false;
+ this.connectionPromise = void 0;
+ this.onclose?.();
+ this.closeAllSubscriptions("relay connection errored");
+ };
+ this.ws.onclose = (ev) => {
+ clearTimeout(this.connectionTimeoutHandle);
+ reject(ev.message || "websocket closed");
+ this._connected = false;
+ this.connectionPromise = void 0;
+ this.onclose?.();
+ this.closeAllSubscriptions("relay connection closed");
+ };
+ this.ws.onmessage = this._onmessage.bind(this);
+ });
+ return this.connectionPromise;
+ }
+ async waitForPingPong() {
+ return new Promise((res, err) => {
+ ;
+ this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
+ this.ws && this.ws.ping && this.ws.ping();
+ });
+ }
+ async waitForDummyReq() {
+ return new Promise((resolve, _) => {
+ const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
+ oneose: () => {
+ sub.close();
+ resolve(true);
+ },
+ eoseTimeout: this.pingTimeout + 1e3
+ });
+ });
+ }
+ async pingpong() {
+ if (this.ws?.readyState === 1) {
+ const result = await Promise.any([
+ this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
+ new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
+ ]);
+ if (result) {
+ setTimeout(() => this.pingpong(), this.pingFrequency);
+ } else {
+ this.closeAllSubscriptions("pingpong timed out");
+ this._connected = false;
+ this.onclose?.();
+ this.ws?.close();
+ }
+ }
+ }
+ async runQueue() {
+ this.queueRunning = true;
+ while (true) {
+ if (false === this.handleNext()) {
+ break;
+ }
+ await yieldThread();
+ }
+ this.queueRunning = false;
+ }
+ handleNext() {
+ const json = this.incomingMessageQueue.dequeue();
+ if (!json) {
+ return false;
+ }
+ const subid = getSubscriptionId(json);
+ if (subid) {
+ const so = this.openSubs.get(subid);
+ if (!so) {
+ return;
+ }
+ const id = getHex64(json, "id");
+ const alreadyHave = so.alreadyHaveEvent?.(id);
+ so.receivedEvent?.(this, id);
+ if (alreadyHave) {
+ return;
+ }
+ }
+ try {
+ let data = JSON.parse(json);
+ switch (data[0]) {
+ case "EVENT": {
+ const so = this.openSubs.get(data[1]);
+ const event = data[2];
+ if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
+ so.onevent(event);
+ }
+ return;
+ }
+ case "COUNT": {
+ const id = data[1];
+ const payload = data[2];
+ const cr = this.openCountRequests.get(id);
+ if (cr) {
+ cr.resolve(payload.count);
+ this.openCountRequests.delete(id);
+ }
+ return;
+ }
+ case "EOSE": {
+ const so = this.openSubs.get(data[1]);
+ if (!so)
+ return;
+ so.receivedEose();
+ return;
+ }
+ case "OK": {
+ const id = data[1];
+ const ok = data[2];
+ const reason = data[3];
+ const ep = this.openEventPublishes.get(id);
+ if (ep) {
+ clearTimeout(ep.timeout);
+ if (ok)
+ ep.resolve(reason);
+ else
+ ep.reject(new Error(reason));
+ this.openEventPublishes.delete(id);
+ }
+ return;
+ }
+ case "CLOSED": {
+ const id = data[1];
+ const so = this.openSubs.get(id);
+ if (!so)
+ return;
+ so.closed = true;
+ so.close(data[2]);
+ return;
+ }
+ case "NOTICE":
+ this.onnotice(data[1]);
+ return;
+ case "AUTH": {
+ this.challenge = data[1];
+ return;
+ }
+ }
+ } catch (err) {
+ return;
+ }
+ }
+ async send(message) {
+ if (!this.connectionPromise)
+ throw new SendingOnClosedConnection(message, this.url);
+ this.connectionPromise.then(() => {
+ this.ws?.send(message);
+ });
+ }
+ async auth(signAuthEvent) {
+ const challenge2 = this.challenge;
+ if (!challenge2)
+ throw new Error("can't perform auth, no challenge was received");
+ if (this.authPromise)
+ return this.authPromise;
+ this.authPromise = new Promise(async (resolve, reject) => {
+ try {
+ let evt = await signAuthEvent(makeAuthEvent(this.url, challenge2));
+ let timeout = setTimeout(() => {
+ let ep = this.openEventPublishes.get(evt.id);
+ if (ep) {
+ ep.reject(new Error("auth timed out"));
+ this.openEventPublishes.delete(evt.id);
+ }
+ }, this.publishTimeout);
+ this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
+ this.send('["AUTH",' + JSON.stringify(evt) + "]");
+ } catch (err) {
+ console.warn("subscribe auth function failed:", err);
+ }
+ });
+ return this.authPromise;
+ }
+ async publish(event) {
+ const ret = new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => {
+ const ep = this.openEventPublishes.get(event.id);
+ if (ep) {
+ ep.reject(new Error("publish timed out"));
+ this.openEventPublishes.delete(event.id);
+ }
+ }, this.publishTimeout);
+ this.openEventPublishes.set(event.id, { resolve, reject, timeout });
+ });
+ this.send('["EVENT",' + JSON.stringify(event) + "]");
+ return ret;
+ }
+ async count(filters, params) {
+ this.serial++;
+ const id = params?.id || "count:" + this.serial;
+ const ret = new Promise((resolve, reject) => {
+ this.openCountRequests.set(id, { resolve, reject });
+ });
+ this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
+ return ret;
+ }
+ subscribe(filters, params) {
+ const subscription = this.prepareSubscription(filters, params);
+ subscription.fire();
+ return subscription;
+ }
+ prepareSubscription(filters, params) {
+ this.serial++;
+ const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
+ const subscription = new Subscription(this, id, filters, params);
+ this.openSubs.set(id, subscription);
+ return subscription;
+ }
+ close() {
+ this.closeAllSubscriptions("relay connection closed by us");
+ this._connected = false;
+ this.onclose?.();
+ this.ws?.close();
+ }
+ _onmessage(ev) {
+ this.incomingMessageQueue.enqueue(ev.data);
+ if (!this.queueRunning) {
+ this.runQueue();
+ }
+ }
+ };
+ var Subscription = class {
+ relay;
+ id;
+ closed = false;
+ eosed = false;
+ filters;
+ alreadyHaveEvent;
+ receivedEvent;
+ onevent;
+ oneose;
+ onclose;
+ eoseTimeout;
+ eoseTimeoutHandle;
+ constructor(relay, id, filters, params) {
+ this.relay = relay;
+ this.filters = filters;
+ this.id = id;
+ this.alreadyHaveEvent = params.alreadyHaveEvent;
+ this.receivedEvent = params.receivedEvent;
+ this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
+ this.oneose = params.oneose;
+ this.onclose = params.onclose;
+ this.onevent = params.onevent || ((event) => {
+ console.warn(
+ `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
+ event
+ );
+ });
+ }
+ fire() {
+ this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
+ this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
+ }
+ receivedEose() {
+ if (this.eosed)
+ return;
+ clearTimeout(this.eoseTimeoutHandle);
+ this.eosed = true;
+ this.oneose?.();
+ }
+ close(reason = "closed by caller") {
+ if (!this.closed && this.relay.connected) {
+ try {
+ this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
+ } catch (err) {
+ if (err instanceof SendingOnClosedConnection) {
+ } else {
+ throw err;
+ }
+ }
+ this.closed = true;
+ }
+ this.relay.openSubs.delete(this.id);
+ this.onclose?.(reason);
+ }
+ };
+
+ // relay.ts
+ var _WebSocket;
+ try {
+ _WebSocket = WebSocket;
+ } catch {
+ }
+ var Relay = class extends AbstractRelay {
+ constructor(url) {
+ super(url, { verifyEvent, websocketImplementation: _WebSocket });
+ }
+ static async connect(url) {
+ const relay = new Relay(url);
+ await relay.connect();
+ return relay;
+ }
+ };
+
+ // abstract-pool.ts
+ var AbstractSimplePool = class {
+ relays = /* @__PURE__ */ new Map();
+ seenOn = /* @__PURE__ */ new Map();
+ trackRelays = false;
+ verifyEvent;
+ enablePing;
+ trustedRelayURLs = /* @__PURE__ */ new Set();
+ _WebSocket;
+ constructor(opts) {
+ this.verifyEvent = opts.verifyEvent;
+ this._WebSocket = opts.websocketImplementation;
+ this.enablePing = opts.enablePing;
+ }
+ async ensureRelay(url, params) {
+ const rawUrl = url;
+ const debugEnabled = typeof window !== "undefined" && !!window.__SIMPLE_POOL_DEBUG__;
+ url = normalizeURL(url);
+ let relay = this.relays.get(url);
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL ensureRelay START", {
+ rawUrl,
+ normalizedUrl: url,
+ hadRelay: !!relay,
+ relayMapKeysBefore: Array.from(this.relays.keys())
+ });
+ }
+ if (!relay) {
+ relay = new AbstractRelay(url, {
+ verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
+ websocketImplementation: this._WebSocket,
+ enablePing: this.enablePing
+ });
+ relay.onclose = () => {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL ensureRelay onclose", {
+ normalizedUrl: url,
+ relayMapKeysBeforeDelete: Array.from(this.relays.keys())
+ });
+ }
+ this.relays.delete(url);
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL ensureRelay onclose complete", {
+ normalizedUrl: url,
+ relayMapKeysAfterDelete: Array.from(this.relays.keys())
+ });
+ }
+ };
+ if (params?.connectionTimeout)
+ relay.connectionTimeout = params.connectionTimeout;
+ this.relays.set(url, relay);
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL ensureRelay created relay", {
+ normalizedUrl: url,
+ relayMapKeysAfterCreate: Array.from(this.relays.keys())
+ });
+ }
+ }
+ await relay.connect();
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL ensureRelay connected", {
+ normalizedUrl: url,
+ relayConnected: relay.connected,
+ relayMapKeysAfterConnect: Array.from(this.relays.keys())
+ });
+ }
+ return relay;
+ }
+ close(relays) {
+ relays.map(normalizeURL).forEach((url) => {
+ this.relays.get(url)?.close();
+ this.relays.delete(url);
+ });
+ }
+ subscribe(relays, filter, params) {
+ params.onauth = params.onauth || params.doauth;
+ const request = [];
+ for (let i2 = 0; i2 < relays.length; i2++) {
+ const url = normalizeURL(relays[i2]);
+ if (!request.find((r) => r.url === url)) {
+ request.push({ url, filter });
+ }
+ }
+ return this.subscribeMap(request, params);
+ }
+ subscribeMany(relays, filters, params) {
+ params.onauth = params.onauth || params.doauth;
+ const debugEnabled = typeof window !== "undefined" && !!window.__SIMPLE_POOL_DEBUG__;
+ const request = [];
+ const uniqUrls = [];
+ for (let i2 = 0; i2 < relays.length; i2++) {
+ const url = normalizeURL(relays[i2]);
+ if (uniqUrls.indexOf(url) === -1) {
+ uniqUrls.push(url);
+ for (let f2 = 0; f2 < filters.length; f2++) {
+ request.push({ url, filter: filters[f2] });
+ }
+ }
+ }
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL subscribeMany", {
+ relaysInput: relays,
+ uniqUrls,
+ filtersCount: filters.length,
+ requestsCount: request.length
+ });
+ }
+ return this.subscribeMap(request, params);
+ }
+ subscribeMap(requests, params) {
+ params.onauth = params.onauth || params.doauth;
+ if (this.trackRelays) {
+ params.receivedEvent = (relay, id) => {
+ let set = this.seenOn.get(id);
+ if (!set) {
+ set = /* @__PURE__ */ new Set();
+ this.seenOn.set(id, set);
+ }
+ set.add(relay);
+ };
+ }
+ const _knownIds = /* @__PURE__ */ new Set();
+ const subs = [];
+ const eosesReceived = [];
+ let handleEose = (i2) => {
+ if (eosesReceived[i2])
+ return;
+ eosesReceived[i2] = true;
+ if (eosesReceived.filter((a) => a).length === requests.length) {
+ params.oneose?.();
+ handleEose = () => {
+ };
+ }
+ };
+ const closesReceived = [];
+ let handleClose = (i2, reason) => {
+ if (closesReceived[i2])
+ return;
+ handleEose(i2);
+ closesReceived[i2] = reason;
+ if (closesReceived.filter((a) => a).length === requests.length) {
+ params.onclose?.(closesReceived);
+ handleClose = () => {
+ };
+ }
+ };
+ const localAlreadyHaveEventHandler = (id) => {
+ if (params.alreadyHaveEvent?.(id)) {
+ return true;
+ }
+ const have = _knownIds.has(id);
+ _knownIds.add(id);
+ return have;
+ };
+ const debugEnabled = typeof window !== "undefined" && !!window.__SIMPLE_POOL_DEBUG__;
+ const allOpened = Promise.all(
+ requests.map(async ({ url, filter }, i2) => {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL subscribeMap request", {
+ index: i2,
+ url,
+ filterKinds: filter?.kinds,
+ hasAuthorFilter: !!filter?.authors,
+ hasPTagFilter: !!filter?.["#p"]
+ });
+ }
+ let relay;
+ try {
+ relay = await this.ensureRelay(url, {
+ connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0
+ });
+ } catch (err) {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL subscribeMap ensureRelay FAILED", {
+ index: i2,
+ url,
+ error: err?.message || String(err)
+ });
+ }
+ handleClose(i2, err?.message || String(err));
+ return;
+ }
+ let subscription = relay.subscribe([filter], {
+ ...params,
+ oneose: () => handleEose(i2),
+ onclose: (reason) => {
+ if (reason.startsWith("auth-required: ") && params.onauth) {
+ relay.auth(params.onauth).then(() => {
+ relay.subscribe([filter], {
+ ...params,
+ oneose: () => handleEose(i2),
+ onclose: (reason2) => {
+ handleClose(i2, reason2);
+ },
+ alreadyHaveEvent: localAlreadyHaveEventHandler,
+ eoseTimeout: params.maxWait
+ });
+ }).catch((err) => {
+ handleClose(i2, `auth was required and attempted, but failed with: ${err}`);
+ });
+ } else {
+ handleClose(i2, reason);
+ }
+ },
+ alreadyHaveEvent: localAlreadyHaveEventHandler,
+ eoseTimeout: params.maxWait
+ });
+ subs.push(subscription);
+ })
+ );
+ return {
+ async close(reason) {
+ await allOpened;
+ subs.forEach((sub) => {
+ sub.close(reason);
+ });
+ }
+ };
+ }
+ subscribeEose(relays, filter, params) {
+ params.onauth = params.onauth || params.doauth;
+ const subcloser = this.subscribe(relays, filter, {
+ ...params,
+ oneose() {
+ subcloser.close("closed automatically on eose");
+ }
+ });
+ return subcloser;
+ }
+ subscribeManyEose(relays, filters, params) {
+ params.onauth = params.onauth || params.doauth;
+ const subcloser = this.subscribeMany(relays, filters, {
+ ...params,
+ oneose() {
+ subcloser.close("closed automatically on eose");
+ }
+ });
+ return subcloser;
+ }
+ async querySync(relays, filter, params) {
+ return new Promise(async (resolve) => {
+ const events = [];
+ this.subscribeEose(relays, filter, {
+ ...params,
+ onevent(event) {
+ events.push(event);
+ },
+ onclose(_) {
+ resolve(events);
+ }
+ });
+ });
+ }
+ async get(relays, filter, params) {
+ filter.limit = 1;
+ const events = await this.querySync(relays, filter, params);
+ events.sort((a, b) => b.created_at - a.created_at);
+ return events[0] || null;
+ }
+ publish(relays, event, options) {
+ const debugEnabled = typeof window !== "undefined" && !!window.__SIMPLE_POOL_DEBUG__;
+ const normalizedRelays = relays.map(normalizeURL);
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL publish START", {
+ relaysInput: relays,
+ normalizedRelays,
+ eventKind: event?.kind,
+ eventId: event?.id
+ });
+ }
+ return normalizedRelays.map(async (url, i2, arr) => {
+ if (arr.indexOf(url) !== i2) {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL publish duplicate URL", { url, index: i2 });
+ }
+ return Promise.reject("duplicate url");
+ }
+ let r = await this.ensureRelay(url);
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL publish ensured relay", {
+ url,
+ relayConnected: r?.connected,
+ relayMapKeys: Array.from(this.relays.keys())
+ });
+ }
+ return r.publish(event).catch(async (err) => {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL publish ERROR", {
+ url,
+ error: err?.message || String(err)
+ });
+ }
+ if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) {
+ await r.auth(options.onauth);
+ return r.publish(event);
+ }
+ throw err;
+ }).then((reason) => {
+ if (debugEnabled) {
+ console.log("🔎 SIMPLE_POOL publish SUCCESS", {
+ url,
+ reason
+ });
+ }
+ if (this.trackRelays) {
+ let set = this.seenOn.get(event.id);
+ if (!set) {
+ set = /* @__PURE__ */ new Set();
+ this.seenOn.set(event.id, set);
+ }
+ set.add(r);
+ }
+ return reason;
+ });
+ });
+ }
+ listConnectionStatus() {
+ const map = /* @__PURE__ */ new Map();
+ this.relays.forEach((relay, url) => map.set(url, relay.connected));
+ return map;
+ }
+ destroy() {
+ this.relays.forEach((conn) => conn.close());
+ this.relays = /* @__PURE__ */ new Map();
+ }
+ };
+
+ // pool.ts
+ var _WebSocket2;
+ try {
+ _WebSocket2 = WebSocket;
+ } catch {
+ }
+ var SimplePool = class extends AbstractSimplePool {
+ constructor(options) {
+ super({ verifyEvent, websocketImplementation: _WebSocket2, ...options });
+ }
+ };
+
+ // nip19.ts
+ var nip19_exports = {};
+ __export(nip19_exports, {
+ BECH32_REGEX: () => BECH32_REGEX,
+ Bech32MaxSize: () => Bech32MaxSize,
+ NostrTypeGuard: () => NostrTypeGuard,
+ decode: () => decode,
+ decodeNostrURI: () => decodeNostrURI,
+ encodeBytes: () => encodeBytes,
+ naddrEncode: () => naddrEncode,
+ neventEncode: () => neventEncode,
+ noteEncode: () => noteEncode,
+ nprofileEncode: () => nprofileEncode,
+ npubEncode: () => npubEncode,
+ nsecEncode: () => nsecEncode
+ });
+
+ // node_modules/@scure/base/lib/esm/index.js
+ function assertNumber(n) {
+ if (!Number.isSafeInteger(n))
+ throw new Error(`Wrong integer: ${n}`);
+ }
+ function chain(...args) {
+ const wrap = (a, b) => (c) => a(b(c));
+ const encode = Array.from(args).reverse().reduce((acc, i2) => acc ? wrap(acc, i2.encode) : i2.encode, void 0);
+ const decode2 = args.reduce((acc, i2) => acc ? wrap(acc, i2.decode) : i2.decode, void 0);
+ return { encode, decode: decode2 };
+ }
+ function alphabet(alphabet2) {
+ return {
+ encode: (digits) => {
+ if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number")
+ throw new Error("alphabet.encode input should be an array of numbers");
+ return digits.map((i2) => {
+ assertNumber(i2);
+ if (i2 < 0 || i2 >= alphabet2.length)
+ throw new Error(`Digit index outside alphabet: ${i2} (alphabet: ${alphabet2.length})`);
+ return alphabet2[i2];
+ });
+ },
+ decode: (input) => {
+ if (!Array.isArray(input) || input.length && typeof input[0] !== "string")
+ throw new Error("alphabet.decode input should be array of strings");
+ return input.map((letter) => {
+ if (typeof letter !== "string")
+ throw new Error(`alphabet.decode: not string element=${letter}`);
+ const index = alphabet2.indexOf(letter);
+ if (index === -1)
+ throw new Error(`Unknown letter: "${letter}". Allowed: ${alphabet2}`);
+ return index;
+ });
+ }
+ };
+ }
+ function join(separator = "") {
+ if (typeof separator !== "string")
+ throw new Error("join separator should be string");
+ return {
+ encode: (from) => {
+ if (!Array.isArray(from) || from.length && typeof from[0] !== "string")
+ throw new Error("join.encode input should be array of strings");
+ for (let i2 of from)
+ if (typeof i2 !== "string")
+ throw new Error(`join.encode: non-string input=${i2}`);
+ return from.join(separator);
+ },
+ decode: (to) => {
+ if (typeof to !== "string")
+ throw new Error("join.decode input should be string");
+ return to.split(separator);
+ }
+ };
+ }
+ function padding(bits, chr = "=") {
+ assertNumber(bits);
+ if (typeof chr !== "string")
+ throw new Error("padding chr should be string");
+ return {
+ encode(data) {
+ if (!Array.isArray(data) || data.length && typeof data[0] !== "string")
+ throw new Error("padding.encode input should be array of strings");
+ for (let i2 of data)
+ if (typeof i2 !== "string")
+ throw new Error(`padding.encode: non-string input=${i2}`);
+ while (data.length * bits % 8)
+ data.push(chr);
+ return data;
+ },
+ decode(input) {
+ if (!Array.isArray(input) || input.length && typeof input[0] !== "string")
+ throw new Error("padding.encode input should be array of strings");
+ for (let i2 of input)
+ if (typeof i2 !== "string")
+ throw new Error(`padding.decode: non-string input=${i2}`);
+ let end = input.length;
+ if (end * bits % 8)
+ throw new Error("Invalid padding: string should have whole number of bytes");
+ for (; end > 0 && input[end - 1] === chr; end--) {
+ if (!((end - 1) * bits % 8))
+ throw new Error("Invalid padding: string has too much padding");
+ }
+ return input.slice(0, end);
+ }
+ };
+ }
+ function normalize(fn) {
+ if (typeof fn !== "function")
+ throw new Error("normalize fn should be function");
+ return { encode: (from) => from, decode: (to) => fn(to) };
+ }
+ function convertRadix(data, from, to) {
+ if (from < 2)
+ throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`);
+ if (to < 2)
+ throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`);
+ if (!Array.isArray(data))
+ throw new Error("convertRadix: data should be array");
+ if (!data.length)
+ return [];
+ let pos = 0;
+ const res = [];
+ const digits = Array.from(data);
+ digits.forEach((d) => {
+ assertNumber(d);
+ if (d < 0 || d >= from)
+ throw new Error(`Wrong integer: ${d}`);
+ });
+ while (true) {
+ let carry = 0;
+ let done = true;
+ for (let i2 = pos; i2 < digits.length; i2++) {
+ const digit = digits[i2];
+ const digitBase = from * carry + digit;
+ if (!Number.isSafeInteger(digitBase) || from * carry / from !== carry || digitBase - digit !== from * carry) {
+ throw new Error("convertRadix: carry overflow");
+ }
+ carry = digitBase % to;
+ digits[i2] = Math.floor(digitBase / to);
+ if (!Number.isSafeInteger(digits[i2]) || digits[i2] * to + carry !== digitBase)
+ throw new Error("convertRadix: carry overflow");
+ if (!done)
+ continue;
+ else if (!digits[i2])
+ pos = i2;
+ else
+ done = false;
+ }
+ res.push(carry);
+ if (done)
+ break;
+ }
+ for (let i2 = 0; i2 < data.length - 1 && data[i2] === 0; i2++)
+ res.push(0);
+ return res.reverse();
+ }
+ var gcd = (a, b) => !b ? a : gcd(b, a % b);
+ var radix2carry = (from, to) => from + (to - gcd(from, to));
+ function convertRadix2(data, from, to, padding2) {
+ if (!Array.isArray(data))
+ throw new Error("convertRadix2: data should be array");
+ if (from <= 0 || from > 32)
+ throw new Error(`convertRadix2: wrong from=${from}`);
+ if (to <= 0 || to > 32)
+ throw new Error(`convertRadix2: wrong to=${to}`);
+ if (radix2carry(from, to) > 32) {
+ throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${radix2carry(from, to)}`);
+ }
+ let carry = 0;
+ let pos = 0;
+ const mask = 2 ** to - 1;
+ const res = [];
+ for (const n of data) {
+ assertNumber(n);
+ if (n >= 2 ** from)
+ throw new Error(`convertRadix2: invalid data word=${n} from=${from}`);
+ carry = carry << from | n;
+ if (pos + from > 32)
+ throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`);
+ pos += from;
+ for (; pos >= to; pos -= to)
+ res.push((carry >> pos - to & mask) >>> 0);
+ carry &= 2 ** pos - 1;
+ }
+ carry = carry << to - pos & mask;
+ if (!padding2 && pos >= from)
+ throw new Error("Excess padding");
+ if (!padding2 && carry)
+ throw new Error(`Non-zero padding: ${carry}`);
+ if (padding2 && pos > 0)
+ res.push(carry >>> 0);
+ return res;
+ }
+ function radix(num) {
+ assertNumber(num);
+ return {
+ encode: (bytes4) => {
+ if (!(bytes4 instanceof Uint8Array))
+ throw new Error("radix.encode input should be Uint8Array");
+ return convertRadix(Array.from(bytes4), 2 ** 8, num);
+ },
+ decode: (digits) => {
+ if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number")
+ throw new Error("radix.decode input should be array of strings");
+ return Uint8Array.from(convertRadix(digits, num, 2 ** 8));
+ }
+ };
+ }
+ function radix2(bits, revPadding = false) {
+ assertNumber(bits);
+ if (bits <= 0 || bits > 32)
+ throw new Error("radix2: bits should be in (0..32]");
+ if (radix2carry(8, bits) > 32 || radix2carry(bits, 8) > 32)
+ throw new Error("radix2: carry overflow");
+ return {
+ encode: (bytes4) => {
+ if (!(bytes4 instanceof Uint8Array))
+ throw new Error("radix2.encode input should be Uint8Array");
+ return convertRadix2(Array.from(bytes4), 8, bits, !revPadding);
+ },
+ decode: (digits) => {
+ if (!Array.isArray(digits) || digits.length && typeof digits[0] !== "number")
+ throw new Error("radix2.decode input should be array of strings");
+ return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding));
+ }
+ };
+ }
+ function unsafeWrapper(fn) {
+ if (typeof fn !== "function")
+ throw new Error("unsafeWrapper fn should be function");
+ return function(...args) {
+ try {
+ return fn.apply(null, args);
+ } catch (e) {
+ }
+ };
+ }
+ function checksum(len, fn) {
+ assertNumber(len);
+ if (typeof fn !== "function")
+ throw new Error("checksum fn should be function");
+ return {
+ encode(data) {
+ if (!(data instanceof Uint8Array))
+ throw new Error("checksum.encode: input should be Uint8Array");
+ const checksum2 = fn(data).slice(0, len);
+ const res = new Uint8Array(data.length + len);
+ res.set(data);
+ res.set(checksum2, data.length);
+ return res;
+ },
+ decode(data) {
+ if (!(data instanceof Uint8Array))
+ throw new Error("checksum.decode: input should be Uint8Array");
+ const payload = data.slice(0, -len);
+ const newChecksum = fn(payload).slice(0, len);
+ const oldChecksum = data.slice(-len);
+ for (let i2 = 0; i2 < len; i2++)
+ if (newChecksum[i2] !== oldChecksum[i2])
+ throw new Error("Invalid checksum");
+ return payload;
+ }
+ };
+ }
+ var utils = { alphabet, chain, checksum, radix, radix2, join, padding };
+ var base16 = chain(radix2(4), alphabet("0123456789ABCDEF"), join(""));
+ var base32 = chain(radix2(5), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"), padding(5), join(""));
+ var base32hex = chain(radix2(5), alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"), padding(5), join(""));
+ var base32crockford = chain(radix2(5), alphabet("0123456789ABCDEFGHJKMNPQRSTVWXYZ"), join(""), normalize((s) => s.toUpperCase().replace(/O/g, "0").replace(/[IL]/g, "1")));
+ var base64 = chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding(6), join(""));
+ var base64url = chain(radix2(6), alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"), padding(6), join(""));
+ var genBase58 = (abc) => chain(radix(58), alphabet(abc), join(""));
+ var base58 = genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
+ var base58flickr = genBase58("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ");
+ var base58xrp = genBase58("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");
+ var XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11];
+ var base58xmr = {
+ encode(data) {
+ let res = "";
+ for (let i2 = 0; i2 < data.length; i2 += 8) {
+ const block = data.subarray(i2, i2 + 8);
+ res += base58.encode(block).padStart(XMR_BLOCK_LEN[block.length], "1");
+ }
+ return res;
+ },
+ decode(str) {
+ let res = [];
+ for (let i2 = 0; i2 < str.length; i2 += 11) {
+ const slice = str.slice(i2, i2 + 11);
+ const blockLen = XMR_BLOCK_LEN.indexOf(slice.length);
+ const block = base58.decode(slice);
+ for (let j = 0; j < block.length - blockLen; j++) {
+ if (block[j] !== 0)
+ throw new Error("base58xmr: wrong padding");
+ }
+ res = res.concat(Array.from(block.slice(block.length - blockLen)));
+ }
+ return Uint8Array.from(res);
+ }
+ };
+ var base58check = (sha2563) => chain(checksum(4, (data) => sha2563(sha2563(data))), base58);
+ var BECH_ALPHABET = chain(alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), join(""));
+ var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
+ function bech32Polymod(pre) {
+ const b = pre >> 25;
+ let chk = (pre & 33554431) << 5;
+ for (let i2 = 0; i2 < POLYMOD_GENERATORS.length; i2++) {
+ if ((b >> i2 & 1) === 1)
+ chk ^= POLYMOD_GENERATORS[i2];
+ }
+ return chk;
+ }
+ function bechChecksum(prefix, words, encodingConst = 1) {
+ const len = prefix.length;
+ let chk = 1;
+ for (let i2 = 0; i2 < len; i2++) {
+ const c = prefix.charCodeAt(i2);
+ if (c < 33 || c > 126)
+ throw new Error(`Invalid prefix (${prefix})`);
+ chk = bech32Polymod(chk) ^ c >> 5;
+ }
+ chk = bech32Polymod(chk);
+ for (let i2 = 0; i2 < len; i2++)
+ chk = bech32Polymod(chk) ^ prefix.charCodeAt(i2) & 31;
+ for (let v of words)
+ chk = bech32Polymod(chk) ^ v;
+ for (let i2 = 0; i2 < 6; i2++)
+ chk = bech32Polymod(chk);
+ chk ^= encodingConst;
+ return BECH_ALPHABET.encode(convertRadix2([chk % 2 ** 30], 30, 5, false));
+ }
+ function genBech32(encoding) {
+ const ENCODING_CONST = encoding === "bech32" ? 1 : 734539939;
+ const _words = radix2(5);
+ const fromWords = _words.decode;
+ const toWords = _words.encode;
+ const fromWordsUnsafe = unsafeWrapper(fromWords);
+ function encode(prefix, words, limit2 = 90) {
+ if (typeof prefix !== "string")
+ throw new Error(`bech32.encode prefix should be string, not ${typeof prefix}`);
+ if (!Array.isArray(words) || words.length && typeof words[0] !== "number")
+ throw new Error(`bech32.encode words should be array of numbers, not ${typeof words}`);
+ const actualLength = prefix.length + 7 + words.length;
+ if (limit2 !== false && actualLength > limit2)
+ throw new TypeError(`Length ${actualLength} exceeds limit ${limit2}`);
+ prefix = prefix.toLowerCase();
+ return `${prefix}1${BECH_ALPHABET.encode(words)}${bechChecksum(prefix, words, ENCODING_CONST)}`;
+ }
+ function decode2(str, limit2 = 90) {
+ if (typeof str !== "string")
+ throw new Error(`bech32.decode input should be string, not ${typeof str}`);
+ if (str.length < 8 || limit2 !== false && str.length > limit2)
+ throw new TypeError(`Wrong string length: ${str.length} (${str}). Expected (8..${limit2})`);
+ const lowered = str.toLowerCase();
+ if (str !== lowered && str !== str.toUpperCase())
+ throw new Error(`String must be lowercase or uppercase`);
+ str = lowered;
+ const sepIndex = str.lastIndexOf("1");
+ if (sepIndex === 0 || sepIndex === -1)
+ throw new Error(`Letter "1" must be present between prefix and data only`);
+ const prefix = str.slice(0, sepIndex);
+ const _words2 = str.slice(sepIndex + 1);
+ if (_words2.length < 6)
+ throw new Error("Data must be at least 6 characters long");
+ const words = BECH_ALPHABET.decode(_words2).slice(0, -6);
+ const sum = bechChecksum(prefix, words, ENCODING_CONST);
+ if (!_words2.endsWith(sum))
+ throw new Error(`Invalid checksum in ${str}: expected "${sum}"`);
+ return { prefix, words };
+ }
+ const decodeUnsafe = unsafeWrapper(decode2);
+ function decodeToBytes(str) {
+ const { prefix, words } = decode2(str, false);
+ return { prefix, words, bytes: fromWords(words) };
+ }
+ return { encode, decode: decode2, decodeToBytes, decodeUnsafe, fromWords, fromWordsUnsafe, toWords };
+ }
+ var bech32 = genBech32("bech32");
+ var bech32m = genBech32("bech32m");
+ var utf8 = {
+ encode: (data) => new TextDecoder().decode(data),
+ decode: (str) => new TextEncoder().encode(str)
+ };
+ var hex = chain(radix2(4), alphabet("0123456789abcdef"), join(""), normalize((s) => {
+ if (typeof s !== "string" || s.length % 2)
+ throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`);
+ return s.toLowerCase();
+ }));
+ var CODERS = {
+ utf8,
+ hex,
+ base16,
+ base32,
+ base64,
+ base64url,
+ base58,
+ base58xmr
+ };
+ var coderTypeError = `Invalid encoding type. Available types: ${Object.keys(CODERS).join(", ")}`;
+
+ // nip19.ts
+ var NostrTypeGuard = {
+ isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
+ isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
+ isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""),
+ isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""),
+ isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""),
+ isNote: (value) => /^note1[a-z\d]+$/.test(value || ""),
+ isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "")
+ };
+ var Bech32MaxSize = 5e3;
+ var BECH32_REGEX = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/;
+ function integerToUint8Array(number4) {
+ const uint8Array = new Uint8Array(4);
+ uint8Array[0] = number4 >> 24 & 255;
+ uint8Array[1] = number4 >> 16 & 255;
+ uint8Array[2] = number4 >> 8 & 255;
+ uint8Array[3] = number4 & 255;
+ return uint8Array;
+ }
+ function decodeNostrURI(nip19code) {
+ try {
+ if (nip19code.startsWith("nostr:"))
+ nip19code = nip19code.substring(6);
+ return decode(nip19code);
+ } catch (_err) {
+ return { type: "invalid", data: null };
+ }
+ }
+ function decode(code) {
+ let { prefix, words } = bech32.decode(code, Bech32MaxSize);
+ let data = new Uint8Array(bech32.fromWords(words));
+ switch (prefix) {
+ case "nprofile": {
+ let tlv = parseTLV(data);
+ if (!tlv[0]?.[0])
+ throw new Error("missing TLV 0 for nprofile");
+ if (tlv[0][0].length !== 32)
+ throw new Error("TLV 0 should be 32 bytes");
+ return {
+ type: "nprofile",
+ data: {
+ pubkey: bytesToHex2(tlv[0][0]),
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
+ }
+ };
+ }
+ case "nevent": {
+ let tlv = parseTLV(data);
+ if (!tlv[0]?.[0])
+ throw new Error("missing TLV 0 for nevent");
+ if (tlv[0][0].length !== 32)
+ throw new Error("TLV 0 should be 32 bytes");
+ if (tlv[2] && tlv[2][0].length !== 32)
+ throw new Error("TLV 2 should be 32 bytes");
+ if (tlv[3] && tlv[3][0].length !== 4)
+ throw new Error("TLV 3 should be 4 bytes");
+ return {
+ type: "nevent",
+ data: {
+ id: bytesToHex2(tlv[0][0]),
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
+ author: tlv[2]?.[0] ? bytesToHex2(tlv[2][0]) : void 0,
+ kind: tlv[3]?.[0] ? parseInt(bytesToHex2(tlv[3][0]), 16) : void 0
+ }
+ };
+ }
+ case "naddr": {
+ let tlv = parseTLV(data);
+ if (!tlv[0]?.[0])
+ throw new Error("missing TLV 0 for naddr");
+ if (!tlv[2]?.[0])
+ throw new Error("missing TLV 2 for naddr");
+ if (tlv[2][0].length !== 32)
+ throw new Error("TLV 2 should be 32 bytes");
+ if (!tlv[3]?.[0])
+ throw new Error("missing TLV 3 for naddr");
+ if (tlv[3][0].length !== 4)
+ throw new Error("TLV 3 should be 4 bytes");
+ return {
+ type: "naddr",
+ data: {
+ identifier: utf8Decoder.decode(tlv[0][0]),
+ pubkey: bytesToHex2(tlv[2][0]),
+ kind: parseInt(bytesToHex2(tlv[3][0]), 16),
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
+ }
+ };
+ }
+ case "nsec":
+ return { type: prefix, data };
+ case "npub":
+ case "note":
+ return { type: prefix, data: bytesToHex2(data) };
+ default:
+ throw new Error(`unknown prefix ${prefix}`);
+ }
+ }
+ function parseTLV(data) {
+ let result = {};
+ let rest = data;
+ while (rest.length > 0) {
+ let t = rest[0];
+ let l = rest[1];
+ let v = rest.slice(2, 2 + l);
+ rest = rest.slice(2 + l);
+ if (v.length < l)
+ throw new Error(`not enough data to read on TLV ${t}`);
+ result[t] = result[t] || [];
+ result[t].push(v);
+ }
+ return result;
+ }
+ function nsecEncode(key) {
+ return encodeBytes("nsec", key);
+ }
+ function npubEncode(hex2) {
+ return encodeBytes("npub", hexToBytes2(hex2));
+ }
+ function noteEncode(hex2) {
+ return encodeBytes("note", hexToBytes2(hex2));
+ }
+ function encodeBech32(prefix, data) {
+ let words = bech32.toWords(data);
+ return bech32.encode(prefix, words, Bech32MaxSize);
+ }
+ function encodeBytes(prefix, bytes4) {
+ return encodeBech32(prefix, bytes4);
+ }
+ function nprofileEncode(profile) {
+ let data = encodeTLV({
+ 0: [hexToBytes2(profile.pubkey)],
+ 1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
+ });
+ return encodeBech32("nprofile", data);
+ }
+ function neventEncode(event) {
+ let kindArray;
+ if (event.kind !== void 0) {
+ kindArray = integerToUint8Array(event.kind);
+ }
+ let data = encodeTLV({
+ 0: [hexToBytes2(event.id)],
+ 1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
+ 2: event.author ? [hexToBytes2(event.author)] : [],
+ 3: kindArray ? [new Uint8Array(kindArray)] : []
+ });
+ return encodeBech32("nevent", data);
+ }
+ function naddrEncode(addr) {
+ let kind = new ArrayBuffer(4);
+ new DataView(kind).setUint32(0, addr.kind, false);
+ let data = encodeTLV({
+ 0: [utf8Encoder.encode(addr.identifier)],
+ 1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
+ 2: [hexToBytes2(addr.pubkey)],
+ 3: [new Uint8Array(kind)]
+ });
+ return encodeBech32("naddr", data);
+ }
+ function encodeTLV(tlv) {
+ let entries = [];
+ Object.entries(tlv).reverse().forEach(([t, vs]) => {
+ vs.forEach((v) => {
+ let entry = new Uint8Array(v.length + 2);
+ entry.set([parseInt(t)], 0);
+ entry.set([v.length], 1);
+ entry.set(v, 2);
+ entries.push(entry);
+ });
+ });
+ return concatBytes3(...entries);
+ }
+
+ // references.ts
+ var mentionRegex = /\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]/g;
+ function parseReferences(evt) {
+ let references = [];
+ for (let ref of evt.content.matchAll(mentionRegex)) {
+ if (ref[2]) {
+ try {
+ let { type, data } = decode(ref[1]);
+ switch (type) {
+ case "npub": {
+ references.push({
+ text: ref[0],
+ profile: { pubkey: data, relays: [] }
+ });
+ break;
+ }
+ case "nprofile": {
+ references.push({
+ text: ref[0],
+ profile: data
+ });
+ break;
+ }
+ case "note": {
+ references.push({
+ text: ref[0],
+ event: { id: data, relays: [] }
+ });
+ break;
+ }
+ case "nevent": {
+ references.push({
+ text: ref[0],
+ event: data
+ });
+ break;
+ }
+ case "naddr": {
+ references.push({
+ text: ref[0],
+ address: data
+ });
+ break;
+ }
+ }
+ } catch (err) {
+ }
+ } else if (ref[3]) {
+ let idx = parseInt(ref[3], 10);
+ let tag = evt.tags[idx];
+ if (!tag)
+ continue;
+ switch (tag[0]) {
+ case "p": {
+ references.push({
+ text: ref[0],
+ profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }
+ });
+ break;
+ }
+ case "e": {
+ references.push({
+ text: ref[0],
+ event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] }
+ });
+ break;
+ }
+ case "a": {
+ try {
+ let [kind, pubkey, identifier] = tag[1].split(":");
+ references.push({
+ text: ref[0],
+ address: {
+ identifier,
+ pubkey,
+ kind: parseInt(kind, 10),
+ relays: tag[2] ? [tag[2]] : []
+ }
+ });
+ } catch (err) {
+ }
+ break;
+ }
+ }
+ }
+ }
+ return references;
+ }
+
+ // nip04.ts
+ var nip04_exports = {};
+ __export(nip04_exports, {
+ decrypt: () => decrypt2,
+ encrypt: () => encrypt2
+ });
+
+ // node_modules/@noble/ciphers/esm/_assert.js
+ function number3(n) {
+ if (!Number.isSafeInteger(n) || n < 0)
+ throw new Error(`positive integer expected, not ${n}`);
+ }
+ function bool2(b) {
+ if (typeof b !== "boolean")
+ throw new Error(`boolean expected, not ${b}`);
+ }
+ function isBytes(a) {
+ return a instanceof Uint8Array || a != null && typeof a === "object" && a.constructor.name === "Uint8Array";
+ }
+ function bytes3(b, ...lengths) {
+ if (!isBytes(b))
+ throw new Error("Uint8Array expected");
+ if (lengths.length > 0 && !lengths.includes(b.length))
+ throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`);
+ }
+ function exists3(instance, checkFinished = true) {
+ if (instance.destroyed)
+ throw new Error("Hash instance has been destroyed");
+ if (checkFinished && instance.finished)
+ throw new Error("Hash#digest() has already been called");
+ }
+ function output3(out, instance) {
+ bytes3(out);
+ const min = instance.outputLen;
+ if (out.length < min) {
+ throw new Error(`digestInto() expects output buffer of length at least ${min}`);
+ }
+ }
+
+ // node_modules/@noble/ciphers/esm/utils.js
+ var u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
+ var u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
+ var createView3 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
+ var isLE3 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
+ if (!isLE3)
+ throw new Error("Non little-endian hardware is not supported");
+ function utf8ToBytes4(str) {
+ if (typeof str !== "string")
+ throw new Error(`string expected, got ${typeof str}`);
+ return new Uint8Array(new TextEncoder().encode(str));
+ }
+ function toBytes3(data) {
+ if (typeof data === "string")
+ data = utf8ToBytes4(data);
+ else if (isBytes(data))
+ data = data.slice();
+ else
+ throw new Error(`Uint8Array expected, got ${typeof data}`);
+ return data;
+ }
+ function checkOpts2(defaults, opts) {
+ if (opts == null || typeof opts !== "object")
+ throw new Error("options must be defined");
+ const merged = Object.assign(defaults, opts);
+ return merged;
+ }
+ function equalBytes2(a, b) {
+ if (a.length !== b.length)
+ return false;
+ let diff = 0;
+ for (let i2 = 0; i2 < a.length; i2++)
+ diff |= a[i2] ^ b[i2];
+ return diff === 0;
+ }
+ var wrapCipher = (params, c) => {
+ Object.assign(c, params);
+ return c;
+ };
+ function setBigUint643(view, byteOffset, value, isLE4) {
+ if (typeof view.setBigUint64 === "function")
+ return view.setBigUint64(byteOffset, value, isLE4);
+ const _32n2 = BigInt(32);
+ const _u32_max = BigInt(4294967295);
+ const wh = Number(value >> _32n2 & _u32_max);
+ const wl = Number(value & _u32_max);
+ const h = isLE4 ? 4 : 0;
+ const l = isLE4 ? 0 : 4;
+ view.setUint32(byteOffset + h, wh, isLE4);
+ view.setUint32(byteOffset + l, wl, isLE4);
+ }
+
+ // node_modules/@noble/ciphers/esm/_polyval.js
+ var BLOCK_SIZE = 16;
+ var ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
+ var ZEROS32 = u32(ZEROS16);
+ var POLY = 225;
+ var mul2 = (s0, s1, s2, s3) => {
+ const hiBit = s3 & 1;
+ return {
+ s3: s2 << 31 | s3 >>> 1,
+ s2: s1 << 31 | s2 >>> 1,
+ s1: s0 << 31 | s1 >>> 1,
+ s0: s0 >>> 1 ^ POLY << 24 & -(hiBit & 1)
+ };
+ };
+ var swapLE = (n) => (n >>> 0 & 255) << 24 | (n >>> 8 & 255) << 16 | (n >>> 16 & 255) << 8 | n >>> 24 & 255 | 0;
+ function _toGHASHKey(k) {
+ k.reverse();
+ const hiBit = k[15] & 1;
+ let carry = 0;
+ for (let i2 = 0; i2 < k.length; i2++) {
+ const t = k[i2];
+ k[i2] = t >>> 1 | carry;
+ carry = (t & 1) << 7;
+ }
+ k[0] ^= -hiBit & 225;
+ return k;
+ }
+ var estimateWindow = (bytes4) => {
+ if (bytes4 > 64 * 1024)
+ return 8;
+ if (bytes4 > 1024)
+ return 4;
+ return 2;
+ };
+ var GHASH = class {
+ constructor(key, expectedLength) {
+ this.blockLen = BLOCK_SIZE;
+ this.outputLen = BLOCK_SIZE;
+ this.s0 = 0;
+ this.s1 = 0;
+ this.s2 = 0;
+ this.s3 = 0;
+ this.finished = false;
+ key = toBytes3(key);
+ bytes3(key, 16);
+ const kView = createView3(key);
+ let k0 = kView.getUint32(0, false);
+ let k1 = kView.getUint32(4, false);
+ let k2 = kView.getUint32(8, false);
+ let k3 = kView.getUint32(12, false);
+ const doubles = [];
+ for (let i2 = 0; i2 < 128; i2++) {
+ doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
+ ({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2(k0, k1, k2, k3));
+ }
+ const W = estimateWindow(expectedLength || 1024);
+ if (![1, 2, 4, 8].includes(W))
+ throw new Error(`ghash: wrong window size=${W}, should be 2, 4 or 8`);
+ this.W = W;
+ const bits = 128;
+ const windows = bits / W;
+ const windowSize = this.windowSize = 2 ** W;
+ const items = [];
+ for (let w = 0; w < windows; w++) {
+ for (let byte = 0; byte < windowSize; byte++) {
+ let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
+ for (let j = 0; j < W; j++) {
+ const bit = byte >>> W - j - 1 & 1;
+ if (!bit)
+ continue;
+ const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
+ s0 ^= d0, s1 ^= d1, s2 ^= d2, s3 ^= d3;
+ }
+ items.push({ s0, s1, s2, s3 });
+ }
+ }
+ this.t = items;
+ }
+ _updateBlock(s0, s1, s2, s3) {
+ s0 ^= this.s0, s1 ^= this.s1, s2 ^= this.s2, s3 ^= this.s3;
+ const { W, t, windowSize } = this;
+ let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
+ const mask = (1 << W) - 1;
+ let w = 0;
+ for (const num of [s0, s1, s2, s3]) {
+ for (let bytePos = 0; bytePos < 4; bytePos++) {
+ const byte = num >>> 8 * bytePos & 255;
+ for (let bitPos = 8 / W - 1; bitPos >= 0; bitPos--) {
+ const bit = byte >>> W * bitPos & mask;
+ const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
+ o0 ^= e0, o1 ^= e1, o2 ^= e2, o3 ^= e3;
+ w += 1;
+ }
+ }
+ }
+ this.s0 = o0;
+ this.s1 = o1;
+ this.s2 = o2;
+ this.s3 = o3;
+ }
+ update(data) {
+ data = toBytes3(data);
+ exists3(this);
+ const b32 = u32(data);
+ const blocks = Math.floor(data.length / BLOCK_SIZE);
+ const left = data.length % BLOCK_SIZE;
+ for (let i2 = 0; i2 < blocks; i2++) {
+ this._updateBlock(b32[i2 * 4 + 0], b32[i2 * 4 + 1], b32[i2 * 4 + 2], b32[i2 * 4 + 3]);
+ }
+ if (left) {
+ ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
+ this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]);
+ ZEROS32.fill(0);
+ }
+ return this;
+ }
+ destroy() {
+ const { t } = this;
+ for (const elm of t) {
+ elm.s0 = 0, elm.s1 = 0, elm.s2 = 0, elm.s3 = 0;
+ }
+ }
+ digestInto(out) {
+ exists3(this);
+ output3(out, this);
+ this.finished = true;
+ const { s0, s1, s2, s3 } = this;
+ const o32 = u32(out);
+ o32[0] = s0;
+ o32[1] = s1;
+ o32[2] = s2;
+ o32[3] = s3;
+ return out;
+ }
+ digest() {
+ const res = new Uint8Array(BLOCK_SIZE);
+ this.digestInto(res);
+ this.destroy();
+ return res;
+ }
+ };
+ var Polyval = class extends GHASH {
+ constructor(key, expectedLength) {
+ key = toBytes3(key);
+ const ghKey = _toGHASHKey(key.slice());
+ super(ghKey, expectedLength);
+ ghKey.fill(0);
+ }
+ update(data) {
+ data = toBytes3(data);
+ exists3(this);
+ const b32 = u32(data);
+ const left = data.length % BLOCK_SIZE;
+ const blocks = Math.floor(data.length / BLOCK_SIZE);
+ for (let i2 = 0; i2 < blocks; i2++) {
+ this._updateBlock(swapLE(b32[i2 * 4 + 3]), swapLE(b32[i2 * 4 + 2]), swapLE(b32[i2 * 4 + 1]), swapLE(b32[i2 * 4 + 0]));
+ }
+ if (left) {
+ ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
+ this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0]));
+ ZEROS32.fill(0);
+ }
+ return this;
+ }
+ digestInto(out) {
+ exists3(this);
+ output3(out, this);
+ this.finished = true;
+ const { s0, s1, s2, s3 } = this;
+ const o32 = u32(out);
+ o32[0] = s0;
+ o32[1] = s1;
+ o32[2] = s2;
+ o32[3] = s3;
+ return out.reverse();
+ }
+ };
+ function wrapConstructorWithKey(hashCons) {
+ const hashC = (msg, key) => hashCons(key, msg.length).update(toBytes3(msg)).digest();
+ const tmp = hashCons(new Uint8Array(16), 0);
+ hashC.outputLen = tmp.outputLen;
+ hashC.blockLen = tmp.blockLen;
+ hashC.create = (key, expectedLength) => hashCons(key, expectedLength);
+ return hashC;
+ }
+ var ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength));
+ var polyval = wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength));
+
+ // node_modules/@noble/ciphers/esm/aes.js
+ var BLOCK_SIZE2 = 16;
+ var BLOCK_SIZE32 = 4;
+ var EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE2);
+ var POLY2 = 283;
+ function mul22(n) {
+ return n << 1 ^ POLY2 & -(n >> 7);
+ }
+ function mul(a, b) {
+ let res = 0;
+ for (; b > 0; b >>= 1) {
+ res ^= a & -(b & 1);
+ a = mul22(a);
+ }
+ return res;
+ }
+ var sbox = /* @__PURE__ */ (() => {
+ let t = new Uint8Array(256);
+ for (let i2 = 0, x = 1; i2 < 256; i2++, x ^= mul22(x))
+ t[i2] = x;
+ const box = new Uint8Array(256);
+ box[0] = 99;
+ for (let i2 = 0; i2 < 255; i2++) {
+ let x = t[255 - i2];
+ x |= x << 8;
+ box[t[i2]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
+ }
+ return box;
+ })();
+ var invSbox = /* @__PURE__ */ sbox.map((_, j) => sbox.indexOf(j));
+ var rotr32_8 = (n) => n << 24 | n >>> 8;
+ var rotl32_8 = (n) => n << 8 | n >>> 24;
+ function genTtable(sbox2, fn) {
+ if (sbox2.length !== 256)
+ throw new Error("Wrong sbox length");
+ const T0 = new Uint32Array(256).map((_, j) => fn(sbox2[j]));
+ const T1 = T0.map(rotl32_8);
+ const T2 = T1.map(rotl32_8);
+ const T3 = T2.map(rotl32_8);
+ const T01 = new Uint32Array(256 * 256);
+ const T23 = new Uint32Array(256 * 256);
+ const sbox22 = new Uint16Array(256 * 256);
+ for (let i2 = 0; i2 < 256; i2++) {
+ for (let j = 0; j < 256; j++) {
+ const idx = i2 * 256 + j;
+ T01[idx] = T0[i2] ^ T1[j];
+ T23[idx] = T2[i2] ^ T3[j];
+ sbox22[idx] = sbox2[i2] << 8 | sbox2[j];
+ }
+ }
+ return { sbox: sbox2, sbox2: sbox22, T0, T1, T2, T3, T01, T23 };
+ }
+ var tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => mul(s, 3) << 24 | s << 16 | s << 8 | mul(s, 2));
+ var tableDecoding = /* @__PURE__ */ genTtable(invSbox, (s) => mul(s, 11) << 24 | mul(s, 13) << 16 | mul(s, 9) << 8 | mul(s, 14));
+ var xPowers = /* @__PURE__ */ (() => {
+ const p = new Uint8Array(16);
+ for (let i2 = 0, x = 1; i2 < 16; i2++, x = mul22(x))
+ p[i2] = x;
+ return p;
+ })();
+ function expandKeyLE(key) {
+ bytes3(key);
+ const len = key.length;
+ if (![16, 24, 32].includes(len))
+ throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`);
+ const { sbox2 } = tableEncoding;
+ const k32 = u32(key);
+ const Nk = k32.length;
+ const subByte = (n) => applySbox(sbox2, n, n, n, n);
+ const xk = new Uint32Array(len + 28);
+ xk.set(k32);
+ for (let i2 = Nk; i2 < xk.length; i2++) {
+ let t = xk[i2 - 1];
+ if (i2 % Nk === 0)
+ t = subByte(rotr32_8(t)) ^ xPowers[i2 / Nk - 1];
+ else if (Nk > 6 && i2 % Nk === 4)
+ t = subByte(t);
+ xk[i2] = xk[i2 - Nk] ^ t;
+ }
+ return xk;
+ }
+ function expandKeyDecLE(key) {
+ const encKey = expandKeyLE(key);
+ const xk = encKey.slice();
+ const Nk = encKey.length;
+ const { sbox2 } = tableEncoding;
+ const { T0, T1, T2, T3 } = tableDecoding;
+ for (let i2 = 0; i2 < Nk; i2 += 4) {
+ for (let j = 0; j < 4; j++)
+ xk[i2 + j] = encKey[Nk - i2 - 4 + j];
+ }
+ encKey.fill(0);
+ for (let i2 = 4; i2 < Nk - 4; i2++) {
+ const x = xk[i2];
+ const w = applySbox(sbox2, x, x, x, x);
+ xk[i2] = T0[w & 255] ^ T1[w >>> 8 & 255] ^ T2[w >>> 16 & 255] ^ T3[w >>> 24];
+ }
+ return xk;
+ }
+ function apply0123(T01, T23, s0, s1, s2, s3) {
+ return T01[s0 << 8 & 65280 | s1 >>> 8 & 255] ^ T23[s2 >>> 8 & 65280 | s3 >>> 24 & 255];
+ }
+ function applySbox(sbox2, s0, s1, s2, s3) {
+ return sbox2[s0 & 255 | s1 & 65280] | sbox2[s2 >>> 16 & 255 | s3 >>> 16 & 65280] << 16;
+ }
+ function encrypt(xk, s0, s1, s2, s3) {
+ const { sbox2, T01, T23 } = tableEncoding;
+ let k = 0;
+ s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
+ const rounds = xk.length / 4 - 2;
+ for (let i2 = 0; i2 < rounds; i2++) {
+ const t02 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
+ const t12 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
+ const t22 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
+ const t32 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
+ s0 = t02, s1 = t12, s2 = t22, s3 = t32;
+ }
+ const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
+ const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
+ const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
+ const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
+ return { s0: t0, s1: t1, s2: t2, s3: t3 };
+ }
+ function decrypt(xk, s0, s1, s2, s3) {
+ const { sbox2, T01, T23 } = tableDecoding;
+ let k = 0;
+ s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
+ const rounds = xk.length / 4 - 2;
+ for (let i2 = 0; i2 < rounds; i2++) {
+ const t02 = xk[k++] ^ apply0123(T01, T23, s0, s3, s2, s1);
+ const t12 = xk[k++] ^ apply0123(T01, T23, s1, s0, s3, s2);
+ const t22 = xk[k++] ^ apply0123(T01, T23, s2, s1, s0, s3);
+ const t32 = xk[k++] ^ apply0123(T01, T23, s3, s2, s1, s0);
+ s0 = t02, s1 = t12, s2 = t22, s3 = t32;
+ }
+ const t0 = xk[k++] ^ applySbox(sbox2, s0, s3, s2, s1);
+ const t1 = xk[k++] ^ applySbox(sbox2, s1, s0, s3, s2);
+ const t2 = xk[k++] ^ applySbox(sbox2, s2, s1, s0, s3);
+ const t3 = xk[k++] ^ applySbox(sbox2, s3, s2, s1, s0);
+ return { s0: t0, s1: t1, s2: t2, s3: t3 };
+ }
+ function getDst(len, dst) {
+ if (!dst)
+ return new Uint8Array(len);
+ bytes3(dst);
+ if (dst.length < len)
+ throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`);
+ return dst;
+ }
+ function ctrCounter(xk, nonce, src, dst) {
+ bytes3(nonce, BLOCK_SIZE2);
+ bytes3(src);
+ const srcLen = src.length;
+ dst = getDst(srcLen, dst);
+ const ctr3 = nonce;
+ const c32 = u32(ctr3);
+ let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
+ const src32 = u32(src);
+ const dst32 = u32(dst);
+ for (let i2 = 0; i2 + 4 <= src32.length; i2 += 4) {
+ dst32[i2 + 0] = src32[i2 + 0] ^ s0;
+ dst32[i2 + 1] = src32[i2 + 1] ^ s1;
+ dst32[i2 + 2] = src32[i2 + 2] ^ s2;
+ dst32[i2 + 3] = src32[i2 + 3] ^ s3;
+ let carry = 1;
+ for (let i3 = ctr3.length - 1; i3 >= 0; i3--) {
+ carry = carry + (ctr3[i3] & 255) | 0;
+ ctr3[i3] = carry & 255;
+ carry >>>= 8;
+ }
+ ({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
+ }
+ const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32);
+ if (start < srcLen) {
+ const b32 = new Uint32Array([s0, s1, s2, s3]);
+ const buf = u8(b32);
+ for (let i2 = start, pos = 0; i2 < srcLen; i2++, pos++)
+ dst[i2] = src[i2] ^ buf[pos];
+ }
+ return dst;
+ }
+ function ctr32(xk, isLE4, nonce, src, dst) {
+ bytes3(nonce, BLOCK_SIZE2);
+ bytes3(src);
+ dst = getDst(src.length, dst);
+ const ctr3 = nonce;
+ const c32 = u32(ctr3);
+ const view = createView3(ctr3);
+ const src32 = u32(src);
+ const dst32 = u32(dst);
+ const ctrPos = isLE4 ? 0 : 12;
+ const srcLen = src.length;
+ let ctrNum = view.getUint32(ctrPos, isLE4);
+ let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
+ for (let i2 = 0; i2 + 4 <= src32.length; i2 += 4) {
+ dst32[i2 + 0] = src32[i2 + 0] ^ s0;
+ dst32[i2 + 1] = src32[i2 + 1] ^ s1;
+ dst32[i2 + 2] = src32[i2 + 2] ^ s2;
+ dst32[i2 + 3] = src32[i2 + 3] ^ s3;
+ ctrNum = ctrNum + 1 >>> 0;
+ view.setUint32(ctrPos, ctrNum, isLE4);
+ ({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
+ }
+ const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32);
+ if (start < srcLen) {
+ const b32 = new Uint32Array([s0, s1, s2, s3]);
+ const buf = u8(b32);
+ for (let i2 = start, pos = 0; i2 < srcLen; i2++, pos++)
+ dst[i2] = src[i2] ^ buf[pos];
+ }
+ return dst;
+ }
+ var ctr = wrapCipher({ blockSize: 16, nonceLength: 16 }, function ctr2(key, nonce) {
+ bytes3(key);
+ bytes3(nonce, BLOCK_SIZE2);
+ function processCtr(buf, dst) {
+ const xk = expandKeyLE(key);
+ const n = nonce.slice();
+ const out = ctrCounter(xk, n, buf, dst);
+ xk.fill(0);
+ n.fill(0);
+ return out;
+ }
+ return {
+ encrypt: (plaintext, dst) => processCtr(plaintext, dst),
+ decrypt: (ciphertext, dst) => processCtr(ciphertext, dst)
+ };
+ });
+ function validateBlockDecrypt(data) {
+ bytes3(data);
+ if (data.length % BLOCK_SIZE2 !== 0) {
+ throw new Error(`aes/(cbc-ecb).decrypt ciphertext should consist of blocks with size ${BLOCK_SIZE2}`);
+ }
+ }
+ function validateBlockEncrypt(plaintext, pcks5, dst) {
+ let outLen = plaintext.length;
+ const remaining = outLen % BLOCK_SIZE2;
+ if (!pcks5 && remaining !== 0)
+ throw new Error("aec/(cbc-ecb): unpadded plaintext with disabled padding");
+ const b = u32(plaintext);
+ if (pcks5) {
+ let left = BLOCK_SIZE2 - remaining;
+ if (!left)
+ left = BLOCK_SIZE2;
+ outLen = outLen + left;
+ }
+ const out = getDst(outLen, dst);
+ const o = u32(out);
+ return { b, o, out };
+ }
+ function validatePCKS(data, pcks5) {
+ if (!pcks5)
+ return data;
+ const len = data.length;
+ if (!len)
+ throw new Error(`aes/pcks5: empty ciphertext not allowed`);
+ const lastByte = data[len - 1];
+ if (lastByte <= 0 || lastByte > 16)
+ throw new Error(`aes/pcks5: wrong padding byte: ${lastByte}`);
+ const out = data.subarray(0, -lastByte);
+ for (let i2 = 0; i2 < lastByte; i2++)
+ if (data[len - i2 - 1] !== lastByte)
+ throw new Error(`aes/pcks5: wrong padding`);
+ return out;
+ }
+ function padPCKS(left) {
+ const tmp = new Uint8Array(16);
+ const tmp32 = u32(tmp);
+ tmp.set(left);
+ const paddingByte = BLOCK_SIZE2 - left.length;
+ for (let i2 = BLOCK_SIZE2 - paddingByte; i2 < BLOCK_SIZE2; i2++)
+ tmp[i2] = paddingByte;
+ return tmp32;
+ }
+ var ecb = wrapCipher({ blockSize: 16 }, function ecb2(key, opts = {}) {
+ bytes3(key);
+ const pcks5 = !opts.disablePadding;
+ return {
+ encrypt: (plaintext, dst) => {
+ bytes3(plaintext);
+ const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
+ const xk = expandKeyLE(key);
+ let i2 = 0;
+ for (; i2 + 4 <= b.length; ) {
+ const { s0, s1, s2, s3 } = encrypt(xk, b[i2 + 0], b[i2 + 1], b[i2 + 2], b[i2 + 3]);
+ o[i2++] = s0, o[i2++] = s1, o[i2++] = s2, o[i2++] = s3;
+ }
+ if (pcks5) {
+ const tmp32 = padPCKS(plaintext.subarray(i2 * 4));
+ const { s0, s1, s2, s3 } = encrypt(xk, tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
+ o[i2++] = s0, o[i2++] = s1, o[i2++] = s2, o[i2++] = s3;
+ }
+ xk.fill(0);
+ return _out;
+ },
+ decrypt: (ciphertext, dst) => {
+ validateBlockDecrypt(ciphertext);
+ const xk = expandKeyDecLE(key);
+ const out = getDst(ciphertext.length, dst);
+ const b = u32(ciphertext);
+ const o = u32(out);
+ for (let i2 = 0; i2 + 4 <= b.length; ) {
+ const { s0, s1, s2, s3 } = decrypt(xk, b[i2 + 0], b[i2 + 1], b[i2 + 2], b[i2 + 3]);
+ o[i2++] = s0, o[i2++] = s1, o[i2++] = s2, o[i2++] = s3;
+ }
+ xk.fill(0);
+ return validatePCKS(out, pcks5);
+ }
+ };
+ });
+ var cbc = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cbc2(key, iv, opts = {}) {
+ bytes3(key);
+ bytes3(iv, 16);
+ const pcks5 = !opts.disablePadding;
+ return {
+ encrypt: (plaintext, dst) => {
+ const xk = expandKeyLE(key);
+ const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
+ const n32 = u32(iv);
+ let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
+ let i2 = 0;
+ for (; i2 + 4 <= b.length; ) {
+ s0 ^= b[i2 + 0], s1 ^= b[i2 + 1], s2 ^= b[i2 + 2], s3 ^= b[i2 + 3];
+ ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
+ o[i2++] = s0, o[i2++] = s1, o[i2++] = s2, o[i2++] = s3;
+ }
+ if (pcks5) {
+ const tmp32 = padPCKS(plaintext.subarray(i2 * 4));
+ s0 ^= tmp32[0], s1 ^= tmp32[1], s2 ^= tmp32[2], s3 ^= tmp32[3];
+ ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
+ o[i2++] = s0, o[i2++] = s1, o[i2++] = s2, o[i2++] = s3;
+ }
+ xk.fill(0);
+ return _out;
+ },
+ decrypt: (ciphertext, dst) => {
+ validateBlockDecrypt(ciphertext);
+ const xk = expandKeyDecLE(key);
+ const n32 = u32(iv);
+ const out = getDst(ciphertext.length, dst);
+ const b = u32(ciphertext);
+ const o = u32(out);
+ let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
+ for (let i2 = 0; i2 + 4 <= b.length; ) {
+ const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3;
+ s0 = b[i2 + 0], s1 = b[i2 + 1], s2 = b[i2 + 2], s3 = b[i2 + 3];
+ const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt(xk, s0, s1, s2, s3);
+ o[i2++] = o0 ^ ps0, o[i2++] = o1 ^ ps1, o[i2++] = o2 ^ ps2, o[i2++] = o3 ^ ps3;
+ }
+ xk.fill(0);
+ return validatePCKS(out, pcks5);
+ }
+ };
+ });
+ var cfb = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cfb2(key, iv) {
+ bytes3(key);
+ bytes3(iv, 16);
+ function processCfb(src, isEncrypt, dst) {
+ const xk = expandKeyLE(key);
+ const srcLen = src.length;
+ dst = getDst(srcLen, dst);
+ const src32 = u32(src);
+ const dst32 = u32(dst);
+ const next32 = isEncrypt ? dst32 : src32;
+ const n32 = u32(iv);
+ let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
+ for (let i2 = 0; i2 + 4 <= src32.length; ) {
+ const { s0: e0, s1: e1, s2: e2, s3: e3 } = encrypt(xk, s0, s1, s2, s3);
+ dst32[i2 + 0] = src32[i2 + 0] ^ e0;
+ dst32[i2 + 1] = src32[i2 + 1] ^ e1;
+ dst32[i2 + 2] = src32[i2 + 2] ^ e2;
+ dst32[i2 + 3] = src32[i2 + 3] ^ e3;
+ s0 = next32[i2++], s1 = next32[i2++], s2 = next32[i2++], s3 = next32[i2++];
+ }
+ const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32);
+ if (start < srcLen) {
+ ({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
+ const buf = u8(new Uint32Array([s0, s1, s2, s3]));
+ for (let i2 = start, pos = 0; i2 < srcLen; i2++, pos++)
+ dst[i2] = src[i2] ^ buf[pos];
+ buf.fill(0);
+ }
+ xk.fill(0);
+ return dst;
+ }
+ return {
+ encrypt: (plaintext, dst) => processCfb(plaintext, true, dst),
+ decrypt: (ciphertext, dst) => processCfb(ciphertext, false, dst)
+ };
+ });
+ function computeTag(fn, isLE4, key, data, AAD) {
+ const h = fn.create(key, data.length + (AAD?.length || 0));
+ if (AAD)
+ h.update(AAD);
+ h.update(data);
+ const num = new Uint8Array(16);
+ const view = createView3(num);
+ if (AAD)
+ setBigUint643(view, 0, BigInt(AAD.length * 8), isLE4);
+ setBigUint643(view, 8, BigInt(data.length * 8), isLE4);
+ h.update(num);
+ return h.digest();
+ }
+ var gcm = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm2(key, nonce, AAD) {
+ bytes3(nonce);
+ if (nonce.length === 0)
+ throw new Error("aes/gcm: empty nonce");
+ const tagLength = 16;
+ function _computeTag(authKey, tagMask, data) {
+ const tag = computeTag(ghash, false, authKey, data, AAD);
+ for (let i2 = 0; i2 < tagMask.length; i2++)
+ tag[i2] ^= tagMask[i2];
+ return tag;
+ }
+ function deriveKeys() {
+ const xk = expandKeyLE(key);
+ const authKey = EMPTY_BLOCK.slice();
+ const counter = EMPTY_BLOCK.slice();
+ ctr32(xk, false, counter, counter, authKey);
+ if (nonce.length === 12) {
+ counter.set(nonce);
+ } else {
+ const nonceLen = EMPTY_BLOCK.slice();
+ const view = createView3(nonceLen);
+ setBigUint643(view, 8, BigInt(nonce.length * 8), false);
+ ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter);
+ }
+ const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
+ return { xk, authKey, counter, tagMask };
+ }
+ return {
+ encrypt: (plaintext) => {
+ bytes3(plaintext);
+ const { xk, authKey, counter, tagMask } = deriveKeys();
+ const out = new Uint8Array(plaintext.length + tagLength);
+ ctr32(xk, false, counter, plaintext, out);
+ const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
+ out.set(tag, plaintext.length);
+ xk.fill(0);
+ return out;
+ },
+ decrypt: (ciphertext) => {
+ bytes3(ciphertext);
+ if (ciphertext.length < tagLength)
+ throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`);
+ const { xk, authKey, counter, tagMask } = deriveKeys();
+ const data = ciphertext.subarray(0, -tagLength);
+ const passedTag = ciphertext.subarray(-tagLength);
+ const tag = _computeTag(authKey, tagMask, data);
+ if (!equalBytes2(tag, passedTag))
+ throw new Error("aes/gcm: invalid ghash tag");
+ const out = ctr32(xk, false, counter, data);
+ authKey.fill(0);
+ tagMask.fill(0);
+ xk.fill(0);
+ return out;
+ }
+ };
+ });
+ var limit = (name, min, max) => (value) => {
+ if (!Number.isSafeInteger(value) || min > value || value > max)
+ throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
+ };
+ var siv = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function siv2(key, nonce, AAD) {
+ const tagLength = 16;
+ const AAD_LIMIT = limit("AAD", 0, 2 ** 36);
+ const PLAIN_LIMIT = limit("plaintext", 0, 2 ** 36);
+ const NONCE_LIMIT = limit("nonce", 12, 12);
+ const CIPHER_LIMIT = limit("ciphertext", 16, 2 ** 36 + 16);
+ bytes3(nonce);
+ NONCE_LIMIT(nonce.length);
+ if (AAD) {
+ bytes3(AAD);
+ AAD_LIMIT(AAD.length);
+ }
+ function deriveKeys() {
+ const len = key.length;
+ if (len !== 16 && len !== 24 && len !== 32)
+ throw new Error(`key length must be 16, 24 or 32 bytes, got: ${len} bytes`);
+ const xk = expandKeyLE(key);
+ const encKey = new Uint8Array(len);
+ const authKey = new Uint8Array(16);
+ const n32 = u32(nonce);
+ let s0 = 0, s1 = n32[0], s2 = n32[1], s3 = n32[2];
+ let counter = 0;
+ for (const derivedKey of [authKey, encKey].map(u32)) {
+ const d32 = u32(derivedKey);
+ for (let i2 = 0; i2 < d32.length; i2 += 2) {
+ const { s0: o0, s1: o1 } = encrypt(xk, s0, s1, s2, s3);
+ d32[i2 + 0] = o0;
+ d32[i2 + 1] = o1;
+ s0 = ++counter;
+ }
+ }
+ xk.fill(0);
+ return { authKey, encKey: expandKeyLE(encKey) };
+ }
+ function _computeTag(encKey, authKey, data) {
+ const tag = computeTag(polyval, true, authKey, data, AAD);
+ for (let i2 = 0; i2 < 12; i2++)
+ tag[i2] ^= nonce[i2];
+ tag[15] &= 127;
+ const t32 = u32(tag);
+ let s0 = t32[0], s1 = t32[1], s2 = t32[2], s3 = t32[3];
+ ({ s0, s1, s2, s3 } = encrypt(encKey, s0, s1, s2, s3));
+ t32[0] = s0, t32[1] = s1, t32[2] = s2, t32[3] = s3;
+ return tag;
+ }
+ function processSiv(encKey, tag, input) {
+ let block = tag.slice();
+ block[15] |= 128;
+ return ctr32(encKey, true, block, input);
+ }
+ return {
+ encrypt: (plaintext) => {
+ bytes3(plaintext);
+ PLAIN_LIMIT(plaintext.length);
+ const { encKey, authKey } = deriveKeys();
+ const tag = _computeTag(encKey, authKey, plaintext);
+ const out = new Uint8Array(plaintext.length + tagLength);
+ out.set(tag, plaintext.length);
+ out.set(processSiv(encKey, tag, plaintext));
+ encKey.fill(0);
+ authKey.fill(0);
+ return out;
+ },
+ decrypt: (ciphertext) => {
+ bytes3(ciphertext);
+ CIPHER_LIMIT(ciphertext.length);
+ const tag = ciphertext.subarray(-tagLength);
+ const { encKey, authKey } = deriveKeys();
+ const plaintext = processSiv(encKey, tag, ciphertext.subarray(0, -tagLength));
+ const expectedTag = _computeTag(encKey, authKey, plaintext);
+ encKey.fill(0);
+ authKey.fill(0);
+ if (!equalBytes2(tag, expectedTag))
+ throw new Error("invalid polyval tag");
+ return plaintext;
+ }
+ };
+ });
+
+ // nip04.ts
+ function encrypt2(secretKey, pubkey, text) {
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex2(secretKey) : secretKey;
+ const key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+ const normalizedKey = getNormalizedX(key);
+ let iv = Uint8Array.from(randomBytes2(16));
+ let plaintext = utf8Encoder.encode(text);
+ let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
+ let ctb64 = base64.encode(new Uint8Array(ciphertext));
+ let ivb64 = base64.encode(new Uint8Array(iv.buffer));
+ return `${ctb64}?iv=${ivb64}`;
+ }
+ function decrypt2(secretKey, pubkey, data) {
+ const privkey = secretKey instanceof Uint8Array ? bytesToHex2(secretKey) : secretKey;
+ let [ctb64, ivb64] = data.split("?iv=");
+ let key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
+ let normalizedKey = getNormalizedX(key);
+ let iv = base64.decode(ivb64);
+ let ciphertext = base64.decode(ctb64);
+ let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext);
+ return utf8Decoder.decode(plaintext);
+ }
+ function getNormalizedX(key) {
+ return key.slice(1, 33);
+ }
+
+ // nip05.ts
+ var nip05_exports = {};
+ __export(nip05_exports, {
+ NIP05_REGEX: () => NIP05_REGEX,
+ isNip05: () => isNip05,
+ isValid: () => isValid,
+ queryProfile: () => queryProfile,
+ searchDomain: () => searchDomain,
+ useFetchImplementation: () => useFetchImplementation
+ });
+ var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/;
+ var isNip05 = (value) => NIP05_REGEX.test(value || "");
+ var _fetch;
+ try {
+ _fetch = fetch;
+ } catch (_) {
+ null;
+ }
+ function useFetchImplementation(fetchImplementation) {
+ _fetch = fetchImplementation;
+ }
+ async function searchDomain(domain, query = "") {
+ try {
+ const url = `https://${domain}/.well-known/nostr.json?name=${query}`;
+ const res = await _fetch(url, { redirect: "manual" });
+ if (res.status !== 200) {
+ throw Error("Wrong response code");
+ }
+ const json = await res.json();
+ return json.names;
+ } catch (_) {
+ return {};
+ }
+ }
+ async function queryProfile(fullname) {
+ const match = fullname.match(NIP05_REGEX);
+ if (!match)
+ return null;
+ const [, name = "_", domain] = match;
+ try {
+ const url = `https://${domain}/.well-known/nostr.json?name=${name}`;
+ const res = await _fetch(url, { redirect: "manual" });
+ if (res.status !== 200) {
+ throw Error("Wrong response code");
+ }
+ const json = await res.json();
+ const pubkey = json.names[name];
+ return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null;
+ } catch (_e) {
+ return null;
+ }
+ }
+ async function isValid(pubkey, nip05) {
+ const res = await queryProfile(nip05);
+ return res ? res.pubkey === pubkey : false;
+ }
+
+ // nip06.ts
+ var nip06_exports = {};
+ __export(nip06_exports, {
+ accountFromExtendedKey: () => accountFromExtendedKey,
+ accountFromSeedWords: () => accountFromSeedWords,
+ extendedKeysFromSeedWords: () => extendedKeysFromSeedWords,
+ generateSeedWords: () => generateSeedWords,
+ privateKeyFromSeedWords: () => privateKeyFromSeedWords,
+ validateWords: () => validateWords
+ });
+
+ // node_modules/@scure/bip39/esm/wordlists/english.js
+ var wordlist = `abandon
+ability
+able
+about
+above
+absent
+absorb
+abstract
+absurd
+abuse
+access
+accident
+account
+accuse
+achieve
+acid
+acoustic
+acquire
+across
+act
+action
+actor
+actress
+actual
+adapt
+add
+addict
+address
+adjust
+admit
+adult
+advance
+advice
+aerobic
+affair
+afford
+afraid
+again
+age
+agent
+agree
+ahead
+aim
+air
+airport
+aisle
+alarm
+album
+alcohol
+alert
+alien
+all
+alley
+allow
+almost
+alone
+alpha
+already
+also
+alter
+always
+amateur
+amazing
+among
+amount
+amused
+analyst
+anchor
+ancient
+anger
+angle
+angry
+animal
+ankle
+announce
+annual
+another
+answer
+antenna
+antique
+anxiety
+any
+apart
+apology
+appear
+apple
+approve
+april
+arch
+arctic
+area
+arena
+argue
+arm
+armed
+armor
+army
+around
+arrange
+arrest
+arrive
+arrow
+art
+artefact
+artist
+artwork
+ask
+aspect
+assault
+asset
+assist
+assume
+asthma
+athlete
+atom
+attack
+attend
+attitude
+attract
+auction
+audit
+august
+aunt
+author
+auto
+autumn
+average
+avocado
+avoid
+awake
+aware
+away
+awesome
+awful
+awkward
+axis
+baby
+bachelor
+bacon
+badge
+bag
+balance
+balcony
+ball
+bamboo
+banana
+banner
+bar
+barely
+bargain
+barrel
+base
+basic
+basket
+battle
+beach
+bean
+beauty
+because
+become
+beef
+before
+begin
+behave
+behind
+believe
+below
+belt
+bench
+benefit
+best
+betray
+better
+between
+beyond
+bicycle
+bid
+bike
+bind
+biology
+bird
+birth
+bitter
+black
+blade
+blame
+blanket
+blast
+bleak
+bless
+blind
+blood
+blossom
+blouse
+blue
+blur
+blush
+board
+boat
+body
+boil
+bomb
+bone
+bonus
+book
+boost
+border
+boring
+borrow
+boss
+bottom
+bounce
+box
+boy
+bracket
+brain
+brand
+brass
+brave
+bread
+breeze
+brick
+bridge
+brief
+bright
+bring
+brisk
+broccoli
+broken
+bronze
+broom
+brother
+brown
+brush
+bubble
+buddy
+budget
+buffalo
+build
+bulb
+bulk
+bullet
+bundle
+bunker
+burden
+burger
+burst
+bus
+business
+busy
+butter
+buyer
+buzz
+cabbage
+cabin
+cable
+cactus
+cage
+cake
+call
+calm
+camera
+camp
+can
+canal
+cancel
+candy
+cannon
+canoe
+canvas
+canyon
+capable
+capital
+captain
+car
+carbon
+card
+cargo
+carpet
+carry
+cart
+case
+cash
+casino
+castle
+casual
+cat
+catalog
+catch
+category
+cattle
+caught
+cause
+caution
+cave
+ceiling
+celery
+cement
+census
+century
+cereal
+certain
+chair
+chalk
+champion
+change
+chaos
+chapter
+charge
+chase
+chat
+cheap
+check
+cheese
+chef
+cherry
+chest
+chicken
+chief
+child
+chimney
+choice
+choose
+chronic
+chuckle
+chunk
+churn
+cigar
+cinnamon
+circle
+citizen
+city
+civil
+claim
+clap
+clarify
+claw
+clay
+clean
+clerk
+clever
+click
+client
+cliff
+climb
+clinic
+clip
+clock
+clog
+close
+cloth
+cloud
+clown
+club
+clump
+cluster
+clutch
+coach
+coast
+coconut
+code
+coffee
+coil
+coin
+collect
+color
+column
+combine
+come
+comfort
+comic
+common
+company
+concert
+conduct
+confirm
+congress
+connect
+consider
+control
+convince
+cook
+cool
+copper
+copy
+coral
+core
+corn
+correct
+cost
+cotton
+couch
+country
+couple
+course
+cousin
+cover
+coyote
+crack
+cradle
+craft
+cram
+crane
+crash
+crater
+crawl
+crazy
+cream
+credit
+creek
+crew
+cricket
+crime
+crisp
+critic
+crop
+cross
+crouch
+crowd
+crucial
+cruel
+cruise
+crumble
+crunch
+crush
+cry
+crystal
+cube
+culture
+cup
+cupboard
+curious
+current
+curtain
+curve
+cushion
+custom
+cute
+cycle
+dad
+damage
+damp
+dance
+danger
+daring
+dash
+daughter
+dawn
+day
+deal
+debate
+debris
+decade
+december
+decide
+decline
+decorate
+decrease
+deer
+defense
+define
+defy
+degree
+delay
+deliver
+demand
+demise
+denial
+dentist
+deny
+depart
+depend
+deposit
+depth
+deputy
+derive
+describe
+desert
+design
+desk
+despair
+destroy
+detail
+detect
+develop
+device
+devote
+diagram
+dial
+diamond
+diary
+dice
+diesel
+diet
+differ
+digital
+dignity
+dilemma
+dinner
+dinosaur
+direct
+dirt
+disagree
+discover
+disease
+dish
+dismiss
+disorder
+display
+distance
+divert
+divide
+divorce
+dizzy
+doctor
+document
+dog
+doll
+dolphin
+domain
+donate
+donkey
+donor
+door
+dose
+double
+dove
+draft
+dragon
+drama
+drastic
+draw
+dream
+dress
+drift
+drill
+drink
+drip
+drive
+drop
+drum
+dry
+duck
+dumb
+dune
+during
+dust
+dutch
+duty
+dwarf
+dynamic
+eager
+eagle
+early
+earn
+earth
+easily
+east
+easy
+echo
+ecology
+economy
+edge
+edit
+educate
+effort
+egg
+eight
+either
+elbow
+elder
+electric
+elegant
+element
+elephant
+elevator
+elite
+else
+embark
+embody
+embrace
+emerge
+emotion
+employ
+empower
+empty
+enable
+enact
+end
+endless
+endorse
+enemy
+energy
+enforce
+engage
+engine
+enhance
+enjoy
+enlist
+enough
+enrich
+enroll
+ensure
+enter
+entire
+entry
+envelope
+episode
+equal
+equip
+era
+erase
+erode
+erosion
+error
+erupt
+escape
+essay
+essence
+estate
+eternal
+ethics
+evidence
+evil
+evoke
+evolve
+exact
+example
+excess
+exchange
+excite
+exclude
+excuse
+execute
+exercise
+exhaust
+exhibit
+exile
+exist
+exit
+exotic
+expand
+expect
+expire
+explain
+expose
+express
+extend
+extra
+eye
+eyebrow
+fabric
+face
+faculty
+fade
+faint
+faith
+fall
+false
+fame
+family
+famous
+fan
+fancy
+fantasy
+farm
+fashion
+fat
+fatal
+father
+fatigue
+fault
+favorite
+feature
+february
+federal
+fee
+feed
+feel
+female
+fence
+festival
+fetch
+fever
+few
+fiber
+fiction
+field
+figure
+file
+film
+filter
+final
+find
+fine
+finger
+finish
+fire
+firm
+first
+fiscal
+fish
+fit
+fitness
+fix
+flag
+flame
+flash
+flat
+flavor
+flee
+flight
+flip
+float
+flock
+floor
+flower
+fluid
+flush
+fly
+foam
+focus
+fog
+foil
+fold
+follow
+food
+foot
+force
+forest
+forget
+fork
+fortune
+forum
+forward
+fossil
+foster
+found
+fox
+fragile
+frame
+frequent
+fresh
+friend
+fringe
+frog
+front
+frost
+frown
+frozen
+fruit
+fuel
+fun
+funny
+furnace
+fury
+future
+gadget
+gain
+galaxy
+gallery
+game
+gap
+garage
+garbage
+garden
+garlic
+garment
+gas
+gasp
+gate
+gather
+gauge
+gaze
+general
+genius
+genre
+gentle
+genuine
+gesture
+ghost
+giant
+gift
+giggle
+ginger
+giraffe
+girl
+give
+glad
+glance
+glare
+glass
+glide
+glimpse
+globe
+gloom
+glory
+glove
+glow
+glue
+goat
+goddess
+gold
+good
+goose
+gorilla
+gospel
+gossip
+govern
+gown
+grab
+grace
+grain
+grant
+grape
+grass
+gravity
+great
+green
+grid
+grief
+grit
+grocery
+group
+grow
+grunt
+guard
+guess
+guide
+guilt
+guitar
+gun
+gym
+habit
+hair
+half
+hammer
+hamster
+hand
+happy
+harbor
+hard
+harsh
+harvest
+hat
+have
+hawk
+hazard
+head
+health
+heart
+heavy
+hedgehog
+height
+hello
+helmet
+help
+hen
+hero
+hidden
+high
+hill
+hint
+hip
+hire
+history
+hobby
+hockey
+hold
+hole
+holiday
+hollow
+home
+honey
+hood
+hope
+horn
+horror
+horse
+hospital
+host
+hotel
+hour
+hover
+hub
+huge
+human
+humble
+humor
+hundred
+hungry
+hunt
+hurdle
+hurry
+hurt
+husband
+hybrid
+ice
+icon
+idea
+identify
+idle
+ignore
+ill
+illegal
+illness
+image
+imitate
+immense
+immune
+impact
+impose
+improve
+impulse
+inch
+include
+income
+increase
+index
+indicate
+indoor
+industry
+infant
+inflict
+inform
+inhale
+inherit
+initial
+inject
+injury
+inmate
+inner
+innocent
+input
+inquiry
+insane
+insect
+inside
+inspire
+install
+intact
+interest
+into
+invest
+invite
+involve
+iron
+island
+isolate
+issue
+item
+ivory
+jacket
+jaguar
+jar
+jazz
+jealous
+jeans
+jelly
+jewel
+job
+join
+joke
+journey
+joy
+judge
+juice
+jump
+jungle
+junior
+junk
+just
+kangaroo
+keen
+keep
+ketchup
+key
+kick
+kid
+kidney
+kind
+kingdom
+kiss
+kit
+kitchen
+kite
+kitten
+kiwi
+knee
+knife
+knock
+know
+lab
+label
+labor
+ladder
+lady
+lake
+lamp
+language
+laptop
+large
+later
+latin
+laugh
+laundry
+lava
+law
+lawn
+lawsuit
+layer
+lazy
+leader
+leaf
+learn
+leave
+lecture
+left
+leg
+legal
+legend
+leisure
+lemon
+lend
+length
+lens
+leopard
+lesson
+letter
+level
+liar
+liberty
+library
+license
+life
+lift
+light
+like
+limb
+limit
+link
+lion
+liquid
+list
+little
+live
+lizard
+load
+loan
+lobster
+local
+lock
+logic
+lonely
+long
+loop
+lottery
+loud
+lounge
+love
+loyal
+lucky
+luggage
+lumber
+lunar
+lunch
+luxury
+lyrics
+machine
+mad
+magic
+magnet
+maid
+mail
+main
+major
+make
+mammal
+man
+manage
+mandate
+mango
+mansion
+manual
+maple
+marble
+march
+margin
+marine
+market
+marriage
+mask
+mass
+master
+match
+material
+math
+matrix
+matter
+maximum
+maze
+meadow
+mean
+measure
+meat
+mechanic
+medal
+media
+melody
+melt
+member
+memory
+mention
+menu
+mercy
+merge
+merit
+merry
+mesh
+message
+metal
+method
+middle
+midnight
+milk
+million
+mimic
+mind
+minimum
+minor
+minute
+miracle
+mirror
+misery
+miss
+mistake
+mix
+mixed
+mixture
+mobile
+model
+modify
+mom
+moment
+monitor
+monkey
+monster
+month
+moon
+moral
+more
+morning
+mosquito
+mother
+motion
+motor
+mountain
+mouse
+move
+movie
+much
+muffin
+mule
+multiply
+muscle
+museum
+mushroom
+music
+must
+mutual
+myself
+mystery
+myth
+naive
+name
+napkin
+narrow
+nasty
+nation
+nature
+near
+neck
+need
+negative
+neglect
+neither
+nephew
+nerve
+nest
+net
+network
+neutral
+never
+news
+next
+nice
+night
+noble
+noise
+nominee
+noodle
+normal
+north
+nose
+notable
+note
+nothing
+notice
+novel
+now
+nuclear
+number
+nurse
+nut
+oak
+obey
+object
+oblige
+obscure
+observe
+obtain
+obvious
+occur
+ocean
+october
+odor
+off
+offer
+office
+often
+oil
+okay
+old
+olive
+olympic
+omit
+once
+one
+onion
+online
+only
+open
+opera
+opinion
+oppose
+option
+orange
+orbit
+orchard
+order
+ordinary
+organ
+orient
+original
+orphan
+ostrich
+other
+outdoor
+outer
+output
+outside
+oval
+oven
+over
+own
+owner
+oxygen
+oyster
+ozone
+pact
+paddle
+page
+pair
+palace
+palm
+panda
+panel
+panic
+panther
+paper
+parade
+parent
+park
+parrot
+party
+pass
+patch
+path
+patient
+patrol
+pattern
+pause
+pave
+payment
+peace
+peanut
+pear
+peasant
+pelican
+pen
+penalty
+pencil
+people
+pepper
+perfect
+permit
+person
+pet
+phone
+photo
+phrase
+physical
+piano
+picnic
+picture
+piece
+pig
+pigeon
+pill
+pilot
+pink
+pioneer
+pipe
+pistol
+pitch
+pizza
+place
+planet
+plastic
+plate
+play
+please
+pledge
+pluck
+plug
+plunge
+poem
+poet
+point
+polar
+pole
+police
+pond
+pony
+pool
+popular
+portion
+position
+possible
+post
+potato
+pottery
+poverty
+powder
+power
+practice
+praise
+predict
+prefer
+prepare
+present
+pretty
+prevent
+price
+pride
+primary
+print
+priority
+prison
+private
+prize
+problem
+process
+produce
+profit
+program
+project
+promote
+proof
+property
+prosper
+protect
+proud
+provide
+public
+pudding
+pull
+pulp
+pulse
+pumpkin
+punch
+pupil
+puppy
+purchase
+purity
+purpose
+purse
+push
+put
+puzzle
+pyramid
+quality
+quantum
+quarter
+question
+quick
+quit
+quiz
+quote
+rabbit
+raccoon
+race
+rack
+radar
+radio
+rail
+rain
+raise
+rally
+ramp
+ranch
+random
+range
+rapid
+rare
+rate
+rather
+raven
+raw
+razor
+ready
+real
+reason
+rebel
+rebuild
+recall
+receive
+recipe
+record
+recycle
+reduce
+reflect
+reform
+refuse
+region
+regret
+regular
+reject
+relax
+release
+relief
+rely
+remain
+remember
+remind
+remove
+render
+renew
+rent
+reopen
+repair
+repeat
+replace
+report
+require
+rescue
+resemble
+resist
+resource
+response
+result
+retire
+retreat
+return
+reunion
+reveal
+review
+reward
+rhythm
+rib
+ribbon
+rice
+rich
+ride
+ridge
+rifle
+right
+rigid
+ring
+riot
+ripple
+risk
+ritual
+rival
+river
+road
+roast
+robot
+robust
+rocket
+romance
+roof
+rookie
+room
+rose
+rotate
+rough
+round
+route
+royal
+rubber
+rude
+rug
+rule
+run
+runway
+rural
+sad
+saddle
+sadness
+safe
+sail
+salad
+salmon
+salon
+salt
+salute
+same
+sample
+sand
+satisfy
+satoshi
+sauce
+sausage
+save
+say
+scale
+scan
+scare
+scatter
+scene
+scheme
+school
+science
+scissors
+scorpion
+scout
+scrap
+screen
+script
+scrub
+sea
+search
+season
+seat
+second
+secret
+section
+security
+seed
+seek
+segment
+select
+sell
+seminar
+senior
+sense
+sentence
+series
+service
+session
+settle
+setup
+seven
+shadow
+shaft
+shallow
+share
+shed
+shell
+sheriff
+shield
+shift
+shine
+ship
+shiver
+shock
+shoe
+shoot
+shop
+short
+shoulder
+shove
+shrimp
+shrug
+shuffle
+shy
+sibling
+sick
+side
+siege
+sight
+sign
+silent
+silk
+silly
+silver
+similar
+simple
+since
+sing
+siren
+sister
+situate
+six
+size
+skate
+sketch
+ski
+skill
+skin
+skirt
+skull
+slab
+slam
+sleep
+slender
+slice
+slide
+slight
+slim
+slogan
+slot
+slow
+slush
+small
+smart
+smile
+smoke
+smooth
+snack
+snake
+snap
+sniff
+snow
+soap
+soccer
+social
+sock
+soda
+soft
+solar
+soldier
+solid
+solution
+solve
+someone
+song
+soon
+sorry
+sort
+soul
+sound
+soup
+source
+south
+space
+spare
+spatial
+spawn
+speak
+special
+speed
+spell
+spend
+sphere
+spice
+spider
+spike
+spin
+spirit
+split
+spoil
+sponsor
+spoon
+sport
+spot
+spray
+spread
+spring
+spy
+square
+squeeze
+squirrel
+stable
+stadium
+staff
+stage
+stairs
+stamp
+stand
+start
+state
+stay
+steak
+steel
+stem
+step
+stereo
+stick
+still
+sting
+stock
+stomach
+stone
+stool
+story
+stove
+strategy
+street
+strike
+strong
+struggle
+student
+stuff
+stumble
+style
+subject
+submit
+subway
+success
+such
+sudden
+suffer
+sugar
+suggest
+suit
+summer
+sun
+sunny
+sunset
+super
+supply
+supreme
+sure
+surface
+surge
+surprise
+surround
+survey
+suspect
+sustain
+swallow
+swamp
+swap
+swarm
+swear
+sweet
+swift
+swim
+swing
+switch
+sword
+symbol
+symptom
+syrup
+system
+table
+tackle
+tag
+tail
+talent
+talk
+tank
+tape
+target
+task
+taste
+tattoo
+taxi
+teach
+team
+tell
+ten
+tenant
+tennis
+tent
+term
+test
+text
+thank
+that
+theme
+then
+theory
+there
+they
+thing
+this
+thought
+three
+thrive
+throw
+thumb
+thunder
+ticket
+tide
+tiger
+tilt
+timber
+time
+tiny
+tip
+tired
+tissue
+title
+toast
+tobacco
+today
+toddler
+toe
+together
+toilet
+token
+tomato
+tomorrow
+tone
+tongue
+tonight
+tool
+tooth
+top
+topic
+topple
+torch
+tornado
+tortoise
+toss
+total
+tourist
+toward
+tower
+town
+toy
+track
+trade
+traffic
+tragic
+train
+transfer
+trap
+trash
+travel
+tray
+treat
+tree
+trend
+trial
+tribe
+trick
+trigger
+trim
+trip
+trophy
+trouble
+truck
+true
+truly
+trumpet
+trust
+truth
+try
+tube
+tuition
+tumble
+tuna
+tunnel
+turkey
+turn
+turtle
+twelve
+twenty
+twice
+twin
+twist
+two
+type
+typical
+ugly
+umbrella
+unable
+unaware
+uncle
+uncover
+under
+undo
+unfair
+unfold
+unhappy
+uniform
+unique
+unit
+universe
+unknown
+unlock
+until
+unusual
+unveil
+update
+upgrade
+uphold
+upon
+upper
+upset
+urban
+urge
+usage
+use
+used
+useful
+useless
+usual
+utility
+vacant
+vacuum
+vague
+valid
+valley
+valve
+van
+vanish
+vapor
+various
+vast
+vault
+vehicle
+velvet
+vendor
+venture
+venue
+verb
+verify
+version
+very
+vessel
+veteran
+viable
+vibrant
+vicious
+victory
+video
+view
+village
+vintage
+violin
+virtual
+virus
+visa
+visit
+visual
+vital
+vivid
+vocal
+voice
+void
+volcano
+volume
+vote
+voyage
+wage
+wagon
+wait
+walk
+wall
+walnut
+want
+warfare
+warm
+warrior
+wash
+wasp
+waste
+water
+wave
+way
+wealth
+weapon
+wear
+weasel
+weather
+web
+wedding
+weekend
+weird
+welcome
+west
+wet
+whale
+what
+wheat
+wheel
+when
+where
+whip
+whisper
+wide
+width
+wife
+wild
+will
+win
+window
+wine
+wing
+wink
+winner
+winter
+wire
+wisdom
+wise
+wish
+witness
+wolf
+woman
+wonder
+wood
+wool
+word
+work
+world
+worry
+worth
+wrap
+wreck
+wrestle
+wrist
+write
+wrong
+yard
+year
+yellow
+you
+young
+youth
+zebra
+zero
+zone
+zoo`.split("\n");
+
+ // node_modules/@noble/hashes/esm/hmac.js
+ var HMAC2 = class extends Hash2 {
+ constructor(hash3, _key) {
+ super();
+ this.finished = false;
+ this.destroyed = false;
+ assert_default.hash(hash3);
+ const key = toBytes2(_key);
+ this.iHash = hash3.create();
+ if (typeof this.iHash.update !== "function")
+ throw new Error("Expected instance of class which extends utils.Hash");
+ this.blockLen = this.iHash.blockLen;
+ this.outputLen = this.iHash.outputLen;
+ const blockLen = this.blockLen;
+ const pad2 = new Uint8Array(blockLen);
+ pad2.set(key.length > blockLen ? hash3.create().update(key).digest() : key);
+ for (let i2 = 0; i2 < pad2.length; i2++)
+ pad2[i2] ^= 54;
+ this.iHash.update(pad2);
+ this.oHash = hash3.create();
+ for (let i2 = 0; i2 < pad2.length; i2++)
+ pad2[i2] ^= 54 ^ 92;
+ this.oHash.update(pad2);
+ pad2.fill(0);
+ }
+ update(buf) {
+ assert_default.exists(this);
+ this.iHash.update(buf);
+ return this;
+ }
+ digestInto(out) {
+ assert_default.exists(this);
+ assert_default.bytes(out, this.outputLen);
+ this.finished = true;
+ this.iHash.digestInto(out);
+ this.oHash.update(out);
+ this.oHash.digestInto(out);
+ this.destroy();
+ }
+ digest() {
+ const out = new Uint8Array(this.oHash.outputLen);
+ this.digestInto(out);
+ return out;
+ }
+ _cloneInto(to) {
+ to || (to = Object.create(Object.getPrototypeOf(this), {}));
+ const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
+ to = to;
+ to.finished = finished;
+ to.destroyed = destroyed;
+ to.blockLen = blockLen;
+ to.outputLen = outputLen;
+ to.oHash = oHash._cloneInto(to.oHash);
+ to.iHash = iHash._cloneInto(to.iHash);
+ return to;
+ }
+ destroy() {
+ this.destroyed = true;
+ this.oHash.destroy();
+ this.iHash.destroy();
+ }
+ };
+ var hmac2 = (hash3, key, message) => new HMAC2(hash3, key).update(message).digest();
+ hmac2.create = (hash3, key) => new HMAC2(hash3, key);
+
+ // node_modules/@noble/hashes/esm/pbkdf2.js
+ function pbkdf2Init(hash3, _password, _salt, _opts) {
+ assert_default.hash(hash3);
+ const opts = checkOpts({ dkLen: 32, asyncTick: 10 }, _opts);
+ const { c, dkLen, asyncTick } = opts;
+ assert_default.number(c);
+ assert_default.number(dkLen);
+ assert_default.number(asyncTick);
+ if (c < 1)
+ throw new Error("PBKDF2: iterations (c) should be >= 1");
+ const password = toBytes2(_password);
+ const salt2 = toBytes2(_salt);
+ const DK = new Uint8Array(dkLen);
+ const PRF = hmac2.create(hash3, password);
+ const PRFSalt = PRF._cloneInto().update(salt2);
+ return { c, dkLen, asyncTick, DK, PRF, PRFSalt };
+ }
+ function pbkdf2Output(PRF, PRFSalt, DK, prfW, u) {
+ PRF.destroy();
+ PRFSalt.destroy();
+ if (prfW)
+ prfW.destroy();
+ u.fill(0);
+ return DK;
+ }
+ function pbkdf2(hash3, password, salt2, opts) {
+ const { c, dkLen, DK, PRF, PRFSalt } = pbkdf2Init(hash3, password, salt2, opts);
+ let prfW;
+ const arr = new Uint8Array(4);
+ const view = createView2(arr);
+ const u = new Uint8Array(PRF.outputLen);
+ for (let ti = 1, pos = 0; pos < dkLen; ti++, pos += PRF.outputLen) {
+ const Ti = DK.subarray(pos, pos + PRF.outputLen);
+ view.setInt32(0, ti, false);
+ (prfW = PRFSalt._cloneInto(prfW)).update(arr).digestInto(u);
+ Ti.set(u.subarray(0, Ti.length));
+ for (let ui = 1; ui < c; ui++) {
+ PRF._cloneInto(prfW).update(u).digestInto(u);
+ for (let i2 = 0; i2 < Ti.length; i2++)
+ Ti[i2] ^= u[i2];
+ }
+ }
+ return pbkdf2Output(PRF, PRFSalt, DK, prfW, u);
+ }
+
+ // node_modules/@noble/hashes/esm/_u64.js
+ var U32_MASK64 = BigInt(2 ** 32 - 1);
+ var _32n = BigInt(32);
+ function fromBig(n, le = false) {
+ if (le)
+ return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
+ return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
+ }
+ function split(lst, le = false) {
+ let Ah = new Uint32Array(lst.length);
+ let Al = new Uint32Array(lst.length);
+ for (let i2 = 0; i2 < lst.length; i2++) {
+ const { h, l } = fromBig(lst[i2], le);
+ [Ah[i2], Al[i2]] = [h, l];
+ }
+ return [Ah, Al];
+ }
+ var toBig = (h, l) => BigInt(h >>> 0) << _32n | BigInt(l >>> 0);
+ var shrSH = (h, l, s) => h >>> s;
+ var shrSL = (h, l, s) => h << 32 - s | l >>> s;
+ var rotrSH = (h, l, s) => h >>> s | l << 32 - s;
+ var rotrSL = (h, l, s) => h << 32 - s | l >>> s;
+ var rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32;
+ var rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s;
+ var rotr32H = (h, l) => l;
+ var rotr32L = (h, l) => h;
+ var rotlSH = (h, l, s) => h << s | l >>> 32 - s;
+ var rotlSL = (h, l, s) => l << s | h >>> 32 - s;
+ var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
+ var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
+ function add(Ah, Al, Bh, Bl) {
+ const l = (Al >>> 0) + (Bl >>> 0);
+ return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
+ }
+ var add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);
+ var add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0;
+ var add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);
+ var add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0;
+ var add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);
+ var add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
+ var u64 = {
+ fromBig,
+ split,
+ toBig,
+ shrSH,
+ shrSL,
+ rotrSH,
+ rotrSL,
+ rotrBH,
+ rotrBL,
+ rotr32H,
+ rotr32L,
+ rotlSH,
+ rotlSL,
+ rotlBH,
+ rotlBL,
+ add,
+ add3L,
+ add3H,
+ add4L,
+ add4H,
+ add5H,
+ add5L
+ };
+ var u64_default = u64;
+
+ // node_modules/@noble/hashes/esm/sha512.js
+ var [SHA512_Kh, SHA512_Kl] = u64_default.split([
+ "0x428a2f98d728ae22",
+ "0x7137449123ef65cd",
+ "0xb5c0fbcfec4d3b2f",
+ "0xe9b5dba58189dbbc",
+ "0x3956c25bf348b538",
+ "0x59f111f1b605d019",
+ "0x923f82a4af194f9b",
+ "0xab1c5ed5da6d8118",
+ "0xd807aa98a3030242",
+ "0x12835b0145706fbe",
+ "0x243185be4ee4b28c",
+ "0x550c7dc3d5ffb4e2",
+ "0x72be5d74f27b896f",
+ "0x80deb1fe3b1696b1",
+ "0x9bdc06a725c71235",
+ "0xc19bf174cf692694",
+ "0xe49b69c19ef14ad2",
+ "0xefbe4786384f25e3",
+ "0x0fc19dc68b8cd5b5",
+ "0x240ca1cc77ac9c65",
+ "0x2de92c6f592b0275",
+ "0x4a7484aa6ea6e483",
+ "0x5cb0a9dcbd41fbd4",
+ "0x76f988da831153b5",
+ "0x983e5152ee66dfab",
+ "0xa831c66d2db43210",
+ "0xb00327c898fb213f",
+ "0xbf597fc7beef0ee4",
+ "0xc6e00bf33da88fc2",
+ "0xd5a79147930aa725",
+ "0x06ca6351e003826f",
+ "0x142929670a0e6e70",
+ "0x27b70a8546d22ffc",
+ "0x2e1b21385c26c926",
+ "0x4d2c6dfc5ac42aed",
+ "0x53380d139d95b3df",
+ "0x650a73548baf63de",
+ "0x766a0abb3c77b2a8",
+ "0x81c2c92e47edaee6",
+ "0x92722c851482353b",
+ "0xa2bfe8a14cf10364",
+ "0xa81a664bbc423001",
+ "0xc24b8b70d0f89791",
+ "0xc76c51a30654be30",
+ "0xd192e819d6ef5218",
+ "0xd69906245565a910",
+ "0xf40e35855771202a",
+ "0x106aa07032bbd1b8",
+ "0x19a4c116b8d2d0c8",
+ "0x1e376c085141ab53",
+ "0x2748774cdf8eeb99",
+ "0x34b0bcb5e19b48a8",
+ "0x391c0cb3c5c95a63",
+ "0x4ed8aa4ae3418acb",
+ "0x5b9cca4f7763e373",
+ "0x682e6ff3d6b2b8a3",
+ "0x748f82ee5defb2fc",
+ "0x78a5636f43172f60",
+ "0x84c87814a1f0ab72",
+ "0x8cc702081a6439ec",
+ "0x90befffa23631e28",
+ "0xa4506cebde82bde9",
+ "0xbef9a3f7b2c67915",
+ "0xc67178f2e372532b",
+ "0xca273eceea26619c",
+ "0xd186b8c721c0c207",
+ "0xeada7dd6cde0eb1e",
+ "0xf57d4f7fee6ed178",
+ "0x06f067aa72176fba",
+ "0x0a637dc5a2c898a6",
+ "0x113f9804bef90dae",
+ "0x1b710b35131c471b",
+ "0x28db77f523047d84",
+ "0x32caab7b40c72493",
+ "0x3c9ebe0a15c9bebc",
+ "0x431d67c49c100d4c",
+ "0x4cc5d4becb3e42b6",
+ "0x597f299cfc657e2a",
+ "0x5fcb6fab3ad6faec",
+ "0x6c44198c4a475817"
+ ].map((n) => BigInt(n)));
+ var SHA512_W_H = new Uint32Array(80);
+ var SHA512_W_L = new Uint32Array(80);
+ var SHA512 = class extends SHA22 {
+ constructor() {
+ super(128, 64, 16, false);
+ this.Ah = 1779033703 | 0;
+ this.Al = 4089235720 | 0;
+ this.Bh = 3144134277 | 0;
+ this.Bl = 2227873595 | 0;
+ this.Ch = 1013904242 | 0;
+ this.Cl = 4271175723 | 0;
+ this.Dh = 2773480762 | 0;
+ this.Dl = 1595750129 | 0;
+ this.Eh = 1359893119 | 0;
+ this.El = 2917565137 | 0;
+ this.Fh = 2600822924 | 0;
+ this.Fl = 725511199 | 0;
+ this.Gh = 528734635 | 0;
+ this.Gl = 4215389547 | 0;
+ this.Hh = 1541459225 | 0;
+ this.Hl = 327033209 | 0;
+ }
+ get() {
+ const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
+ return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
+ }
+ set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl) {
+ this.Ah = Ah | 0;
+ this.Al = Al | 0;
+ this.Bh = Bh | 0;
+ this.Bl = Bl | 0;
+ this.Ch = Ch | 0;
+ this.Cl = Cl | 0;
+ this.Dh = Dh | 0;
+ this.Dl = Dl | 0;
+ this.Eh = Eh | 0;
+ this.El = El | 0;
+ this.Fh = Fh | 0;
+ this.Fl = Fl | 0;
+ this.Gh = Gh | 0;
+ this.Gl = Gl | 0;
+ this.Hh = Hh | 0;
+ this.Hl = Hl | 0;
+ }
+ process(view, offset) {
+ for (let i2 = 0; i2 < 16; i2++, offset += 4) {
+ SHA512_W_H[i2] = view.getUint32(offset);
+ SHA512_W_L[i2] = view.getUint32(offset += 4);
+ }
+ for (let i2 = 16; i2 < 80; i2++) {
+ const W15h = SHA512_W_H[i2 - 15] | 0;
+ const W15l = SHA512_W_L[i2 - 15] | 0;
+ const s0h = u64_default.rotrSH(W15h, W15l, 1) ^ u64_default.rotrSH(W15h, W15l, 8) ^ u64_default.shrSH(W15h, W15l, 7);
+ const s0l = u64_default.rotrSL(W15h, W15l, 1) ^ u64_default.rotrSL(W15h, W15l, 8) ^ u64_default.shrSL(W15h, W15l, 7);
+ const W2h = SHA512_W_H[i2 - 2] | 0;
+ const W2l = SHA512_W_L[i2 - 2] | 0;
+ const s1h = u64_default.rotrSH(W2h, W2l, 19) ^ u64_default.rotrBH(W2h, W2l, 61) ^ u64_default.shrSH(W2h, W2l, 6);
+ const s1l = u64_default.rotrSL(W2h, W2l, 19) ^ u64_default.rotrBL(W2h, W2l, 61) ^ u64_default.shrSL(W2h, W2l, 6);
+ const SUMl = u64_default.add4L(s0l, s1l, SHA512_W_L[i2 - 7], SHA512_W_L[i2 - 16]);
+ const SUMh = u64_default.add4H(SUMl, s0h, s1h, SHA512_W_H[i2 - 7], SHA512_W_H[i2 - 16]);
+ SHA512_W_H[i2] = SUMh | 0;
+ SHA512_W_L[i2] = SUMl | 0;
+ }
+ let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
+ for (let i2 = 0; i2 < 80; i2++) {
+ const sigma1h = u64_default.rotrSH(Eh, El, 14) ^ u64_default.rotrSH(Eh, El, 18) ^ u64_default.rotrBH(Eh, El, 41);
+ const sigma1l = u64_default.rotrSL(Eh, El, 14) ^ u64_default.rotrSL(Eh, El, 18) ^ u64_default.rotrBL(Eh, El, 41);
+ const CHIh = Eh & Fh ^ ~Eh & Gh;
+ const CHIl = El & Fl ^ ~El & Gl;
+ const T1ll = u64_default.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i2], SHA512_W_L[i2]);
+ const T1h = u64_default.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i2], SHA512_W_H[i2]);
+ const T1l = T1ll | 0;
+ const sigma0h = u64_default.rotrSH(Ah, Al, 28) ^ u64_default.rotrBH(Ah, Al, 34) ^ u64_default.rotrBH(Ah, Al, 39);
+ const sigma0l = u64_default.rotrSL(Ah, Al, 28) ^ u64_default.rotrBL(Ah, Al, 34) ^ u64_default.rotrBL(Ah, Al, 39);
+ const MAJh = Ah & Bh ^ Ah & Ch ^ Bh & Ch;
+ const MAJl = Al & Bl ^ Al & Cl ^ Bl & Cl;
+ Hh = Gh | 0;
+ Hl = Gl | 0;
+ Gh = Fh | 0;
+ Gl = Fl | 0;
+ Fh = Eh | 0;
+ Fl = El | 0;
+ ({ h: Eh, l: El } = u64_default.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
+ Dh = Ch | 0;
+ Dl = Cl | 0;
+ Ch = Bh | 0;
+ Cl = Bl | 0;
+ Bh = Ah | 0;
+ Bl = Al | 0;
+ const All = u64_default.add3L(T1l, sigma0l, MAJl);
+ Ah = u64_default.add3H(All, T1h, sigma0h, MAJh);
+ Al = All | 0;
+ }
+ ({ h: Ah, l: Al } = u64_default.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
+ ({ h: Bh, l: Bl } = u64_default.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
+ ({ h: Ch, l: Cl } = u64_default.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
+ ({ h: Dh, l: Dl } = u64_default.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
+ ({ h: Eh, l: El } = u64_default.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
+ ({ h: Fh, l: Fl } = u64_default.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
+ ({ h: Gh, l: Gl } = u64_default.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
+ ({ h: Hh, l: Hl } = u64_default.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
+ this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
+ }
+ roundClean() {
+ SHA512_W_H.fill(0);
+ SHA512_W_L.fill(0);
+ }
+ destroy() {
+ this.buffer.fill(0);
+ this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ };
+ var SHA512_224 = class extends SHA512 {
+ constructor() {
+ super();
+ this.Ah = 2352822216 | 0;
+ this.Al = 424955298 | 0;
+ this.Bh = 1944164710 | 0;
+ this.Bl = 2312950998 | 0;
+ this.Ch = 502970286 | 0;
+ this.Cl = 855612546 | 0;
+ this.Dh = 1738396948 | 0;
+ this.Dl = 1479516111 | 0;
+ this.Eh = 258812777 | 0;
+ this.El = 2077511080 | 0;
+ this.Fh = 2011393907 | 0;
+ this.Fl = 79989058 | 0;
+ this.Gh = 1067287976 | 0;
+ this.Gl = 1780299464 | 0;
+ this.Hh = 286451373 | 0;
+ this.Hl = 2446758561 | 0;
+ this.outputLen = 28;
+ }
+ };
+ var SHA512_256 = class extends SHA512 {
+ constructor() {
+ super();
+ this.Ah = 573645204 | 0;
+ this.Al = 4230739756 | 0;
+ this.Bh = 2673172387 | 0;
+ this.Bl = 3360449730 | 0;
+ this.Ch = 596883563 | 0;
+ this.Cl = 1867755857 | 0;
+ this.Dh = 2520282905 | 0;
+ this.Dl = 1497426621 | 0;
+ this.Eh = 2519219938 | 0;
+ this.El = 2827943907 | 0;
+ this.Fh = 3193839141 | 0;
+ this.Fl = 1401305490 | 0;
+ this.Gh = 721525244 | 0;
+ this.Gl = 746961066 | 0;
+ this.Hh = 246885852 | 0;
+ this.Hl = 2177182882 | 0;
+ this.outputLen = 32;
+ }
+ };
+ var SHA384 = class extends SHA512 {
+ constructor() {
+ super();
+ this.Ah = 3418070365 | 0;
+ this.Al = 3238371032 | 0;
+ this.Bh = 1654270250 | 0;
+ this.Bl = 914150663 | 0;
+ this.Ch = 2438529370 | 0;
+ this.Cl = 812702999 | 0;
+ this.Dh = 355462360 | 0;
+ this.Dl = 4144912697 | 0;
+ this.Eh = 1731405415 | 0;
+ this.El = 4290775857 | 0;
+ this.Fh = 2394180231 | 0;
+ this.Fl = 1750603025 | 0;
+ this.Gh = 3675008525 | 0;
+ this.Gl = 1694076839 | 0;
+ this.Hh = 1203062813 | 0;
+ this.Hl = 3204075428 | 0;
+ this.outputLen = 48;
+ }
+ };
+ var sha512 = wrapConstructor2(() => new SHA512());
+ var sha512_224 = wrapConstructor2(() => new SHA512_224());
+ var sha512_256 = wrapConstructor2(() => new SHA512_256());
+ var sha384 = wrapConstructor2(() => new SHA384());
+
+ // node_modules/@scure/bip39/esm/index.js
+ var isJapanese = (wordlist2) => wordlist2[0] === "\u3042\u3044\u3053\u304F\u3057\u3093";
+ function nfkd(str) {
+ if (typeof str !== "string")
+ throw new TypeError(`Invalid mnemonic type: ${typeof str}`);
+ return str.normalize("NFKD");
+ }
+ function normalize2(str) {
+ const norm = nfkd(str);
+ const words = norm.split(" ");
+ if (![12, 15, 18, 21, 24].includes(words.length))
+ throw new Error("Invalid mnemonic");
+ return { nfkd: norm, words };
+ }
+ function assertEntropy(entropy) {
+ assert_default.bytes(entropy, 16, 20, 24, 28, 32);
+ }
+ function generateMnemonic(wordlist2, strength = 128) {
+ assert_default.number(strength);
+ if (strength % 32 !== 0 || strength > 256)
+ throw new TypeError("Invalid entropy");
+ return entropyToMnemonic(randomBytes2(strength / 8), wordlist2);
+ }
+ var calcChecksum = (entropy) => {
+ const bitsLeft = 8 - entropy.length / 4;
+ return new Uint8Array([sha2562(entropy)[0] >> bitsLeft << bitsLeft]);
+ };
+ function getCoder(wordlist2) {
+ if (!Array.isArray(wordlist2) || wordlist2.length !== 2048 || typeof wordlist2[0] !== "string")
+ throw new Error("Worlist: expected array of 2048 strings");
+ wordlist2.forEach((i2) => {
+ if (typeof i2 !== "string")
+ throw new Error(`Wordlist: non-string element: ${i2}`);
+ });
+ return utils.chain(utils.checksum(1, calcChecksum), utils.radix2(11, true), utils.alphabet(wordlist2));
+ }
+ function mnemonicToEntropy(mnemonic, wordlist2) {
+ const { words } = normalize2(mnemonic);
+ const entropy = getCoder(wordlist2).decode(words);
+ assertEntropy(entropy);
+ return entropy;
+ }
+ function entropyToMnemonic(entropy, wordlist2) {
+ assertEntropy(entropy);
+ const words = getCoder(wordlist2).encode(entropy);
+ return words.join(isJapanese(wordlist2) ? "\u3000" : " ");
+ }
+ function validateMnemonic(mnemonic, wordlist2) {
+ try {
+ mnemonicToEntropy(mnemonic, wordlist2);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+ var salt = (passphrase) => nfkd(`mnemonic${passphrase}`);
+ function mnemonicToSeedSync(mnemonic, passphrase = "") {
+ return pbkdf2(sha512, normalize2(mnemonic).nfkd, salt(passphrase), { c: 2048, dkLen: 64 });
+ }
+
+ // node_modules/@noble/hashes/esm/ripemd160.js
+ var Rho = new Uint8Array([7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8]);
+ var Id = Uint8Array.from({ length: 16 }, (_, i2) => i2);
+ var Pi = Id.map((i2) => (9 * i2 + 5) % 16);
+ var idxL = [Id];
+ var idxR = [Pi];
+ for (let i2 = 0; i2 < 4; i2++)
+ for (let j of [idxL, idxR])
+ j.push(j[i2].map((k) => Rho[k]));
+ var shifts = [
+ [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
+ [12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
+ [13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
+ [14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
+ [15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5]
+ ].map((i2) => new Uint8Array(i2));
+ var shiftsL = idxL.map((idx, i2) => idx.map((j) => shifts[i2][j]));
+ var shiftsR = idxR.map((idx, i2) => idx.map((j) => shifts[i2][j]));
+ var Kl = new Uint32Array([0, 1518500249, 1859775393, 2400959708, 2840853838]);
+ var Kr = new Uint32Array([1352829926, 1548603684, 1836072691, 2053994217, 0]);
+ var rotl = (word, shift) => word << shift | word >>> 32 - shift;
+ function f(group, x, y, z) {
+ if (group === 0)
+ return x ^ y ^ z;
+ else if (group === 1)
+ return x & y | ~x & z;
+ else if (group === 2)
+ return (x | ~y) ^ z;
+ else if (group === 3)
+ return x & z | y & ~z;
+ else
+ return x ^ (y | ~z);
+ }
+ var BUF = new Uint32Array(16);
+ var RIPEMD160 = class extends SHA22 {
+ constructor() {
+ super(64, 20, 8, true);
+ this.h0 = 1732584193 | 0;
+ this.h1 = 4023233417 | 0;
+ this.h2 = 2562383102 | 0;
+ this.h3 = 271733878 | 0;
+ this.h4 = 3285377520 | 0;
+ }
+ get() {
+ const { h0, h1, h2, h3, h4 } = this;
+ return [h0, h1, h2, h3, h4];
+ }
+ set(h0, h1, h2, h3, h4) {
+ this.h0 = h0 | 0;
+ this.h1 = h1 | 0;
+ this.h2 = h2 | 0;
+ this.h3 = h3 | 0;
+ this.h4 = h4 | 0;
+ }
+ process(view, offset) {
+ for (let i2 = 0; i2 < 16; i2++, offset += 4)
+ BUF[i2] = view.getUint32(offset, true);
+ let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el;
+ for (let group = 0; group < 5; group++) {
+ const rGroup = 4 - group;
+ const hbl = Kl[group], hbr = Kr[group];
+ const rl = idxL[group], rr = idxR[group];
+ const sl = shiftsL[group], sr = shiftsR[group];
+ for (let i2 = 0; i2 < 16; i2++) {
+ const tl = rotl(al + f(group, bl, cl, dl) + BUF[rl[i2]] + hbl, sl[i2]) + el | 0;
+ al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl;
+ }
+ for (let i2 = 0; i2 < 16; i2++) {
+ const tr = rotl(ar + f(rGroup, br, cr, dr) + BUF[rr[i2]] + hbr, sr[i2]) + er | 0;
+ ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr;
+ }
+ }
+ this.set(this.h1 + cl + dr | 0, this.h2 + dl + er | 0, this.h3 + el + ar | 0, this.h4 + al + br | 0, this.h0 + bl + cr | 0);
+ }
+ roundClean() {
+ BUF.fill(0);
+ }
+ destroy() {
+ this.destroyed = true;
+ this.buffer.fill(0);
+ this.set(0, 0, 0, 0, 0);
+ }
+ };
+ var ripemd160 = wrapConstructor2(() => new RIPEMD160());
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/abstract/utils.js
+ var utils_exports3 = {};
+ __export(utils_exports3, {
+ bitGet: () => bitGet2,
+ bitLen: () => bitLen2,
+ bitMask: () => bitMask2,
+ bitSet: () => bitSet2,
+ bytesToHex: () => bytesToHex3,
+ bytesToNumberBE: () => bytesToNumberBE2,
+ bytesToNumberLE: () => bytesToNumberLE2,
+ concatBytes: () => concatBytes4,
+ createHmacDrbg: () => createHmacDrbg2,
+ ensureBytes: () => ensureBytes2,
+ equalBytes: () => equalBytes3,
+ hexToBytes: () => hexToBytes3,
+ hexToNumber: () => hexToNumber2,
+ numberToBytesBE: () => numberToBytesBE2,
+ numberToBytesLE: () => numberToBytesLE2,
+ numberToHexUnpadded: () => numberToHexUnpadded2,
+ numberToVarBytesBE: () => numberToVarBytesBE2,
+ utf8ToBytes: () => utf8ToBytes5,
+ validateObject: () => validateObject2
+ });
+ var _0n6 = BigInt(0);
+ var _1n6 = BigInt(1);
+ var _2n5 = BigInt(2);
+ var u8a4 = (a) => a instanceof Uint8Array;
+ var hexes3 = Array.from({ length: 256 }, (v, i2) => i2.toString(16).padStart(2, "0"));
+ function bytesToHex3(bytes4) {
+ if (!u8a4(bytes4))
+ throw new Error("Uint8Array expected");
+ let hex2 = "";
+ for (let i2 = 0; i2 < bytes4.length; i2++) {
+ hex2 += hexes3[bytes4[i2]];
+ }
+ return hex2;
+ }
+ function numberToHexUnpadded2(num) {
+ const hex2 = num.toString(16);
+ return hex2.length & 1 ? `0${hex2}` : hex2;
+ }
+ function hexToNumber2(hex2) {
+ if (typeof hex2 !== "string")
+ throw new Error("hex string expected, got " + typeof hex2);
+ return BigInt(hex2 === "" ? "0" : `0x${hex2}`);
+ }
+ function hexToBytes3(hex2) {
+ if (typeof hex2 !== "string")
+ throw new Error("hex string expected, got " + typeof hex2);
+ const len = hex2.length;
+ if (len % 2)
+ throw new Error("padded hex string expected, got unpadded hex of length " + len);
+ const array = new Uint8Array(len / 2);
+ for (let i2 = 0; i2 < array.length; i2++) {
+ const j = i2 * 2;
+ const hexByte = hex2.slice(j, j + 2);
+ const byte = Number.parseInt(hexByte, 16);
+ if (Number.isNaN(byte) || byte < 0)
+ throw new Error("Invalid byte sequence");
+ array[i2] = byte;
+ }
+ return array;
+ }
+ function bytesToNumberBE2(bytes4) {
+ return hexToNumber2(bytesToHex3(bytes4));
+ }
+ function bytesToNumberLE2(bytes4) {
+ if (!u8a4(bytes4))
+ throw new Error("Uint8Array expected");
+ return hexToNumber2(bytesToHex3(Uint8Array.from(bytes4).reverse()));
+ }
+ function numberToBytesBE2(n, len) {
+ return hexToBytes3(n.toString(16).padStart(len * 2, "0"));
+ }
+ function numberToBytesLE2(n, len) {
+ return numberToBytesBE2(n, len).reverse();
+ }
+ function numberToVarBytesBE2(n) {
+ return hexToBytes3(numberToHexUnpadded2(n));
+ }
+ function ensureBytes2(title, hex2, expectedLength) {
+ let res;
+ if (typeof hex2 === "string") {
+ try {
+ res = hexToBytes3(hex2);
+ } catch (e) {
+ throw new Error(`${title} must be valid hex string, got "${hex2}". Cause: ${e}`);
+ }
+ } else if (u8a4(hex2)) {
+ res = Uint8Array.from(hex2);
+ } else {
+ throw new Error(`${title} must be hex string or Uint8Array`);
+ }
+ const len = res.length;
+ if (typeof expectedLength === "number" && len !== expectedLength)
+ throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
+ return res;
+ }
+ function concatBytes4(...arrays) {
+ const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
+ let pad2 = 0;
+ arrays.forEach((a) => {
+ if (!u8a4(a))
+ throw new Error("Uint8Array expected");
+ r.set(a, pad2);
+ pad2 += a.length;
+ });
+ return r;
+ }
+ function equalBytes3(b1, b2) {
+ if (b1.length !== b2.length)
+ return false;
+ for (let i2 = 0; i2 < b1.length; i2++)
+ if (b1[i2] !== b2[i2])
+ return false;
+ return true;
+ }
+ function utf8ToBytes5(str) {
+ if (typeof str !== "string")
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
+ return new Uint8Array(new TextEncoder().encode(str));
+ }
+ function bitLen2(n) {
+ let len;
+ for (len = 0; n > _0n6; n >>= _1n6, len += 1)
+ ;
+ return len;
+ }
+ function bitGet2(n, pos) {
+ return n >> BigInt(pos) & _1n6;
+ }
+ var bitSet2 = (n, pos, value) => {
+ return n | (value ? _1n6 : _0n6) << BigInt(pos);
+ };
+ var bitMask2 = (n) => (_2n5 << BigInt(n - 1)) - _1n6;
+ var u8n2 = (data) => new Uint8Array(data);
+ var u8fr2 = (arr) => Uint8Array.from(arr);
+ function createHmacDrbg2(hashLen, qByteLen, hmacFn) {
+ if (typeof hashLen !== "number" || hashLen < 2)
+ throw new Error("hashLen must be a number");
+ if (typeof qByteLen !== "number" || qByteLen < 2)
+ throw new Error("qByteLen must be a number");
+ if (typeof hmacFn !== "function")
+ throw new Error("hmacFn must be a function");
+ let v = u8n2(hashLen);
+ let k = u8n2(hashLen);
+ let i2 = 0;
+ const reset = () => {
+ v.fill(1);
+ k.fill(0);
+ i2 = 0;
+ };
+ const h = (...b) => hmacFn(k, v, ...b);
+ const reseed = (seed = u8n2()) => {
+ k = h(u8fr2([0]), seed);
+ v = h();
+ if (seed.length === 0)
+ return;
+ k = h(u8fr2([1]), seed);
+ v = h();
+ };
+ const gen = () => {
+ if (i2++ >= 1e3)
+ throw new Error("drbg: tried 1000 values");
+ let len = 0;
+ const out = [];
+ while (len < qByteLen) {
+ v = h();
+ const sl = v.slice();
+ out.push(sl);
+ len += v.length;
+ }
+ return concatBytes4(...out);
+ };
+ const genUntil = (seed, pred) => {
+ reset();
+ reseed(seed);
+ let res = void 0;
+ while (!(res = pred(gen())))
+ reseed();
+ reset();
+ return res;
+ };
+ return genUntil;
+ }
+ var validatorFns2 = {
+ bigint: (val) => typeof val === "bigint",
+ function: (val) => typeof val === "function",
+ boolean: (val) => typeof val === "boolean",
+ string: (val) => typeof val === "string",
+ isSafeInteger: (val) => Number.isSafeInteger(val),
+ array: (val) => Array.isArray(val),
+ field: (val, object) => object.Fp.isValid(val),
+ hash: (val) => typeof val === "function" && Number.isSafeInteger(val.outputLen)
+ };
+ function validateObject2(object, validators, optValidators = {}) {
+ const checkField = (fieldName, type, isOptional) => {
+ const checkVal = validatorFns2[type];
+ if (typeof checkVal !== "function")
+ throw new Error(`Invalid validator "${type}", expected function`);
+ const val = object[fieldName];
+ if (isOptional && val === void 0)
+ return;
+ if (!checkVal(val, object)) {
+ throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`);
+ }
+ };
+ for (const [fieldName, type] of Object.entries(validators))
+ checkField(fieldName, type, false);
+ for (const [fieldName, type] of Object.entries(optValidators))
+ checkField(fieldName, type, true);
+ return object;
+ }
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/abstract/modular.js
+ var _0n7 = BigInt(0);
+ var _1n7 = BigInt(1);
+ var _2n6 = BigInt(2);
+ var _3n3 = BigInt(3);
+ var _4n3 = BigInt(4);
+ var _5n2 = BigInt(5);
+ var _8n2 = BigInt(8);
+ var _9n2 = BigInt(9);
+ var _16n2 = BigInt(16);
+ function mod2(a, b) {
+ const result = a % b;
+ return result >= _0n7 ? result : b + result;
+ }
+ function pow3(num, power, modulo) {
+ if (modulo <= _0n7 || power < _0n7)
+ throw new Error("Expected power/modulo > 0");
+ if (modulo === _1n7)
+ return _0n7;
+ let res = _1n7;
+ while (power > _0n7) {
+ if (power & _1n7)
+ res = res * num % modulo;
+ num = num * num % modulo;
+ power >>= _1n7;
+ }
+ return res;
+ }
+ function pow22(x, power, modulo) {
+ let res = x;
+ while (power-- > _0n7) {
+ res *= res;
+ res %= modulo;
+ }
+ return res;
+ }
+ function invert2(number4, modulo) {
+ if (number4 === _0n7 || modulo <= _0n7) {
+ throw new Error(`invert: expected positive integers, got n=${number4} mod=${modulo}`);
+ }
+ let a = mod2(number4, modulo);
+ let b = modulo;
+ let x = _0n7, y = _1n7, u = _1n7, v = _0n7;
+ while (a !== _0n7) {
+ const q = b / a;
+ const r = b % a;
+ const m = x - u * q;
+ const n = y - v * q;
+ b = a, a = r, x = u, y = v, u = m, v = n;
+ }
+ const gcd2 = b;
+ if (gcd2 !== _1n7)
+ throw new Error("invert: does not exist");
+ return mod2(x, modulo);
+ }
+ function tonelliShanks2(P) {
+ const legendreC = (P - _1n7) / _2n6;
+ let Q, S, Z;
+ for (Q = P - _1n7, S = 0; Q % _2n6 === _0n7; Q /= _2n6, S++)
+ ;
+ for (Z = _2n6; Z < P && pow3(Z, legendreC, P) !== P - _1n7; Z++)
+ ;
+ if (S === 1) {
+ const p1div4 = (P + _1n7) / _4n3;
+ return function tonelliFast(Fp3, n) {
+ const root = Fp3.pow(n, p1div4);
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ const Q1div2 = (Q + _1n7) / _2n6;
+ return function tonelliSlow(Fp3, n) {
+ if (Fp3.pow(n, legendreC) === Fp3.neg(Fp3.ONE))
+ throw new Error("Cannot find square root");
+ let r = S;
+ let g = Fp3.pow(Fp3.mul(Fp3.ONE, Z), Q);
+ let x = Fp3.pow(n, Q1div2);
+ let b = Fp3.pow(n, Q);
+ while (!Fp3.eql(b, Fp3.ONE)) {
+ if (Fp3.eql(b, Fp3.ZERO))
+ return Fp3.ZERO;
+ let m = 1;
+ for (let t2 = Fp3.sqr(b); m < r; m++) {
+ if (Fp3.eql(t2, Fp3.ONE))
+ break;
+ t2 = Fp3.sqr(t2);
+ }
+ const ge2 = Fp3.pow(g, _1n7 << BigInt(r - m - 1));
+ g = Fp3.sqr(ge2);
+ x = Fp3.mul(x, ge2);
+ b = Fp3.mul(b, g);
+ r = m;
+ }
+ return x;
+ };
+ }
+ function FpSqrt2(P) {
+ if (P % _4n3 === _3n3) {
+ const p1div4 = (P + _1n7) / _4n3;
+ return function sqrt3mod4(Fp3, n) {
+ const root = Fp3.pow(n, p1div4);
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ if (P % _8n2 === _5n2) {
+ const c1 = (P - _5n2) / _8n2;
+ return function sqrt5mod8(Fp3, n) {
+ const n2 = Fp3.mul(n, _2n6);
+ const v = Fp3.pow(n2, c1);
+ const nv = Fp3.mul(n, v);
+ const i2 = Fp3.mul(Fp3.mul(nv, _2n6), v);
+ const root = Fp3.mul(nv, Fp3.sub(i2, Fp3.ONE));
+ if (!Fp3.eql(Fp3.sqr(root), n))
+ throw new Error("Cannot find square root");
+ return root;
+ };
+ }
+ if (P % _16n2 === _9n2) {
+ }
+ return tonelliShanks2(P);
+ }
+ var FIELD_FIELDS2 = [
+ "create",
+ "isValid",
+ "is0",
+ "neg",
+ "inv",
+ "sqrt",
+ "sqr",
+ "eql",
+ "add",
+ "sub",
+ "mul",
+ "pow",
+ "div",
+ "addN",
+ "subN",
+ "mulN",
+ "sqrN"
+ ];
+ function validateField2(field) {
+ const initial = {
+ ORDER: "bigint",
+ MASK: "bigint",
+ BYTES: "isSafeInteger",
+ BITS: "isSafeInteger"
+ };
+ const opts = FIELD_FIELDS2.reduce((map, val) => {
+ map[val] = "function";
+ return map;
+ }, initial);
+ return validateObject2(field, opts);
+ }
+ function FpPow2(f2, num, power) {
+ if (power < _0n7)
+ throw new Error("Expected power > 0");
+ if (power === _0n7)
+ return f2.ONE;
+ if (power === _1n7)
+ return num;
+ let p = f2.ONE;
+ let d = num;
+ while (power > _0n7) {
+ if (power & _1n7)
+ p = f2.mul(p, d);
+ d = f2.sqr(d);
+ power >>= _1n7;
+ }
+ return p;
+ }
+ function FpInvertBatch2(f2, nums) {
+ const tmp = new Array(nums.length);
+ const lastMultiplied = nums.reduce((acc, num, i2) => {
+ if (f2.is0(num))
+ return acc;
+ tmp[i2] = acc;
+ return f2.mul(acc, num);
+ }, f2.ONE);
+ const inverted = f2.inv(lastMultiplied);
+ nums.reduceRight((acc, num, i2) => {
+ if (f2.is0(num))
+ return acc;
+ tmp[i2] = f2.mul(acc, tmp[i2]);
+ return f2.mul(acc, num);
+ }, inverted);
+ return tmp;
+ }
+ function nLength2(n, nBitLength) {
+ const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length;
+ const nByteLength = Math.ceil(_nBitLength / 8);
+ return { nBitLength: _nBitLength, nByteLength };
+ }
+ function Field2(ORDER, bitLen3, isLE4 = false, redef = {}) {
+ if (ORDER <= _0n7)
+ throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength2(ORDER, bitLen3);
+ if (BYTES > 2048)
+ throw new Error("Field lengths over 2048 bytes are not supported");
+ const sqrtP = FpSqrt2(ORDER);
+ const f2 = Object.freeze({
+ ORDER,
+ BITS,
+ BYTES,
+ MASK: bitMask2(BITS),
+ ZERO: _0n7,
+ ONE: _1n7,
+ create: (num) => mod2(num, ORDER),
+ isValid: (num) => {
+ if (typeof num !== "bigint")
+ throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
+ return _0n7 <= num && num < ORDER;
+ },
+ is0: (num) => num === _0n7,
+ isOdd: (num) => (num & _1n7) === _1n7,
+ neg: (num) => mod2(-num, ORDER),
+ eql: (lhs, rhs) => lhs === rhs,
+ sqr: (num) => mod2(num * num, ORDER),
+ add: (lhs, rhs) => mod2(lhs + rhs, ORDER),
+ sub: (lhs, rhs) => mod2(lhs - rhs, ORDER),
+ mul: (lhs, rhs) => mod2(lhs * rhs, ORDER),
+ pow: (num, power) => FpPow2(f2, num, power),
+ div: (lhs, rhs) => mod2(lhs * invert2(rhs, ORDER), ORDER),
+ sqrN: (num) => num * num,
+ addN: (lhs, rhs) => lhs + rhs,
+ subN: (lhs, rhs) => lhs - rhs,
+ mulN: (lhs, rhs) => lhs * rhs,
+ inv: (num) => invert2(num, ORDER),
+ sqrt: redef.sqrt || ((n) => sqrtP(f2, n)),
+ invertBatch: (lst) => FpInvertBatch2(f2, lst),
+ cmov: (a, b, c) => c ? b : a,
+ toBytes: (num) => isLE4 ? numberToBytesLE2(num, BYTES) : numberToBytesBE2(num, BYTES),
+ fromBytes: (bytes4) => {
+ if (bytes4.length !== BYTES)
+ throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes4.length}`);
+ return isLE4 ? bytesToNumberLE2(bytes4) : bytesToNumberBE2(bytes4);
+ }
+ });
+ return Object.freeze(f2);
+ }
+ function hashToPrivateScalar(hash3, groupOrder, isLE4 = false) {
+ hash3 = ensureBytes2("privateHash", hash3);
+ const hashLen = hash3.length;
+ const minLen = nLength2(groupOrder).nByteLength + 8;
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
+ const num = isLE4 ? bytesToNumberLE2(hash3) : bytesToNumberBE2(hash3);
+ return mod2(num, groupOrder - _1n7) + _1n7;
+ }
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/abstract/curve.js
+ var _0n8 = BigInt(0);
+ var _1n8 = BigInt(1);
+ function wNAF2(c, bits) {
+ const constTimeNegate = (condition, item) => {
+ const neg = item.negate();
+ return condition ? neg : item;
+ };
+ const opts = (W) => {
+ const windows = Math.ceil(bits / W) + 1;
+ const windowSize = 2 ** (W - 1);
+ return { windows, windowSize };
+ };
+ return {
+ constTimeNegate,
+ unsafeLadder(elm, n) {
+ let p = c.ZERO;
+ let d = elm;
+ while (n > _0n8) {
+ if (n & _1n8)
+ p = p.add(d);
+ d = d.double();
+ n >>= _1n8;
+ }
+ return p;
+ },
+ precomputeWindow(elm, W) {
+ const { windows, windowSize } = opts(W);
+ const points = [];
+ let p = elm;
+ let base = p;
+ for (let window = 0; window < windows; window++) {
+ base = p;
+ points.push(base);
+ for (let i2 = 1; i2 < windowSize; i2++) {
+ base = base.add(p);
+ points.push(base);
+ }
+ p = base.double();
+ }
+ return points;
+ },
+ wNAF(W, precomputes, n) {
+ const { windows, windowSize } = opts(W);
+ let p = c.ZERO;
+ let f2 = c.BASE;
+ const mask = BigInt(2 ** W - 1);
+ const maxNumber = 2 ** W;
+ const shiftBy = BigInt(W);
+ for (let window = 0; window < windows; window++) {
+ const offset = window * windowSize;
+ let wbits = Number(n & mask);
+ n >>= shiftBy;
+ if (wbits > windowSize) {
+ wbits -= maxNumber;
+ n += _1n8;
+ }
+ const offset1 = offset;
+ const offset2 = offset + Math.abs(wbits) - 1;
+ const cond1 = window % 2 !== 0;
+ const cond2 = wbits < 0;
+ if (wbits === 0) {
+ f2 = f2.add(constTimeNegate(cond1, precomputes[offset1]));
+ } else {
+ p = p.add(constTimeNegate(cond2, precomputes[offset2]));
+ }
+ }
+ return { p, f: f2 };
+ },
+ wNAFCached(P, precomputesMap, n, transform) {
+ const W = P._WINDOW_SIZE || 1;
+ let comp = precomputesMap.get(P);
+ if (!comp) {
+ comp = this.precomputeWindow(P, W);
+ if (W !== 1) {
+ precomputesMap.set(P, transform(comp));
+ }
+ }
+ return this.wNAF(W, comp, n);
+ }
+ };
+ }
+ function validateBasic2(curve) {
+ validateField2(curve.Fp);
+ validateObject2(curve, {
+ n: "bigint",
+ h: "bigint",
+ Gx: "field",
+ Gy: "field"
+ }, {
+ nBitLength: "isSafeInteger",
+ nByteLength: "isSafeInteger"
+ });
+ return Object.freeze({
+ ...nLength2(curve.n, curve.nBitLength),
+ ...curve,
+ ...{ p: curve.Fp.ORDER }
+ });
+ }
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/abstract/weierstrass.js
+ function validatePointOpts2(curve) {
+ const opts = validateBasic2(curve);
+ validateObject2(opts, {
+ a: "field",
+ b: "field"
+ }, {
+ allowedPrivateKeyLengths: "array",
+ wrapPrivateKey: "boolean",
+ isTorsionFree: "function",
+ clearCofactor: "function",
+ allowInfinityPoint: "boolean",
+ fromBytes: "function",
+ toBytes: "function"
+ });
+ const { endo, Fp: Fp3, a } = opts;
+ if (endo) {
+ if (!Fp3.eql(a, Fp3.ZERO)) {
+ throw new Error("Endomorphism can only be defined for Koblitz curves that have a=0");
+ }
+ if (typeof endo !== "object" || typeof endo.beta !== "bigint" || typeof endo.splitScalar !== "function") {
+ throw new Error("Expected endomorphism with beta: bigint and splitScalar: function");
+ }
+ }
+ return Object.freeze({ ...opts });
+ }
+ var { bytesToNumberBE: b2n2, hexToBytes: h2b2 } = utils_exports3;
+ var DER2 = {
+ Err: class DERErr2 extends Error {
+ constructor(m = "") {
+ super(m);
+ }
+ },
+ _parseInt(data) {
+ const { Err: E } = DER2;
+ if (data.length < 2 || data[0] !== 2)
+ throw new E("Invalid signature integer tag");
+ const len = data[1];
+ const res = data.subarray(2, len + 2);
+ if (!len || res.length !== len)
+ throw new E("Invalid signature integer: wrong length");
+ if (res[0] & 128)
+ throw new E("Invalid signature integer: negative");
+ if (res[0] === 0 && !(res[1] & 128))
+ throw new E("Invalid signature integer: unnecessary leading zero");
+ return { d: b2n2(res), l: data.subarray(len + 2) };
+ },
+ toSig(hex2) {
+ const { Err: E } = DER2;
+ const data = typeof hex2 === "string" ? h2b2(hex2) : hex2;
+ if (!(data instanceof Uint8Array))
+ throw new Error("ui8a expected");
+ let l = data.length;
+ if (l < 2 || data[0] != 48)
+ throw new E("Invalid signature tag");
+ if (data[1] !== l - 2)
+ throw new E("Invalid signature: incorrect length");
+ const { d: r, l: sBytes } = DER2._parseInt(data.subarray(2));
+ const { d: s, l: rBytesLeft } = DER2._parseInt(sBytes);
+ if (rBytesLeft.length)
+ throw new E("Invalid signature: left bytes after parsing");
+ return { r, s };
+ },
+ hexFromSig(sig) {
+ const slice = (s2) => Number.parseInt(s2[0], 16) & 8 ? "00" + s2 : s2;
+ const h = (num) => {
+ const hex2 = num.toString(16);
+ return hex2.length & 1 ? `0${hex2}` : hex2;
+ };
+ const s = slice(h(sig.s));
+ const r = slice(h(sig.r));
+ const shl = s.length / 2;
+ const rhl = r.length / 2;
+ const sl = h(shl);
+ const rl = h(rhl);
+ return `30${h(rhl + shl + 4)}02${rl}${r}02${sl}${s}`;
+ }
+ };
+ var _0n9 = BigInt(0);
+ var _1n9 = BigInt(1);
+ var _2n7 = BigInt(2);
+ var _3n4 = BigInt(3);
+ var _4n4 = BigInt(4);
+ function weierstrassPoints2(opts) {
+ const CURVE = validatePointOpts2(opts);
+ const { Fp: Fp3 } = CURVE;
+ const toBytes4 = CURVE.toBytes || ((c, point, isCompressed) => {
+ const a = point.toAffine();
+ return concatBytes4(Uint8Array.from([4]), Fp3.toBytes(a.x), Fp3.toBytes(a.y));
+ });
+ const fromBytes = CURVE.fromBytes || ((bytes4) => {
+ const tail = bytes4.subarray(1);
+ const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES));
+ const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES));
+ return { x, y };
+ });
+ function weierstrassEquation(x) {
+ const { a, b } = CURVE;
+ const x2 = Fp3.sqr(x);
+ const x3 = Fp3.mul(x2, x);
+ return Fp3.add(Fp3.add(x3, Fp3.mul(x, a)), b);
+ }
+ if (!Fp3.eql(Fp3.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
+ throw new Error("bad generator point: equation left != right");
+ function isWithinCurveOrder(num) {
+ return typeof num === "bigint" && _0n9 < num && num < CURVE.n;
+ }
+ function assertGE(num) {
+ if (!isWithinCurveOrder(num))
+ throw new Error("Expected valid bigint: 0 < bigint < curve.n");
+ }
+ function normPrivateKeyToScalar(key) {
+ const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
+ if (lengths && typeof key !== "bigint") {
+ if (key instanceof Uint8Array)
+ key = bytesToHex3(key);
+ if (typeof key !== "string" || !lengths.includes(key.length))
+ throw new Error("Invalid key");
+ key = key.padStart(nByteLength * 2, "0");
+ }
+ let num;
+ try {
+ num = typeof key === "bigint" ? key : bytesToNumberBE2(ensureBytes2("private key", key, nByteLength));
+ } catch (error) {
+ throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
+ }
+ if (wrapPrivateKey)
+ num = mod2(num, n);
+ assertGE(num);
+ return num;
+ }
+ const pointPrecomputes = /* @__PURE__ */ new Map();
+ function assertPrjPoint(other) {
+ if (!(other instanceof Point4))
+ throw new Error("ProjectivePoint expected");
+ }
+ class Point4 {
+ constructor(px, py, pz) {
+ this.px = px;
+ this.py = py;
+ this.pz = pz;
+ if (px == null || !Fp3.isValid(px))
+ throw new Error("x required");
+ if (py == null || !Fp3.isValid(py))
+ throw new Error("y required");
+ if (pz == null || !Fp3.isValid(pz))
+ throw new Error("z required");
+ }
+ static fromAffine(p) {
+ const { x, y } = p || {};
+ if (!p || !Fp3.isValid(x) || !Fp3.isValid(y))
+ throw new Error("invalid affine point");
+ if (p instanceof Point4)
+ throw new Error("projective point not allowed");
+ const is0 = (i2) => Fp3.eql(i2, Fp3.ZERO);
+ if (is0(x) && is0(y))
+ return Point4.ZERO;
+ return new Point4(x, y, Fp3.ONE);
+ }
+ get x() {
+ return this.toAffine().x;
+ }
+ get y() {
+ return this.toAffine().y;
+ }
+ static normalizeZ(points) {
+ const toInv = Fp3.invertBatch(points.map((p) => p.pz));
+ return points.map((p, i2) => p.toAffine(toInv[i2])).map(Point4.fromAffine);
+ }
+ static fromHex(hex2) {
+ const P = Point4.fromAffine(fromBytes(ensureBytes2("pointHex", hex2)));
+ P.assertValidity();
+ return P;
+ }
+ static fromPrivateKey(privateKey) {
+ return Point4.BASE.multiply(normPrivateKeyToScalar(privateKey));
+ }
+ _setWindowSize(windowSize) {
+ this._WINDOW_SIZE = windowSize;
+ pointPrecomputes.delete(this);
+ }
+ assertValidity() {
+ if (this.is0()) {
+ if (CURVE.allowInfinityPoint)
+ return;
+ throw new Error("bad point: ZERO");
+ }
+ const { x, y } = this.toAffine();
+ if (!Fp3.isValid(x) || !Fp3.isValid(y))
+ throw new Error("bad point: x or y not FE");
+ const left = Fp3.sqr(y);
+ const right = weierstrassEquation(x);
+ if (!Fp3.eql(left, right))
+ throw new Error("bad point: equation left != right");
+ if (!this.isTorsionFree())
+ throw new Error("bad point: not in prime-order subgroup");
+ }
+ hasEvenY() {
+ const { y } = this.toAffine();
+ if (Fp3.isOdd)
+ return !Fp3.isOdd(y);
+ throw new Error("Field doesn't support isOdd");
+ }
+ equals(other) {
+ assertPrjPoint(other);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ const { px: X2, py: Y2, pz: Z2 } = other;
+ const U1 = Fp3.eql(Fp3.mul(X1, Z2), Fp3.mul(X2, Z1));
+ const U2 = Fp3.eql(Fp3.mul(Y1, Z2), Fp3.mul(Y2, Z1));
+ return U1 && U2;
+ }
+ negate() {
+ return new Point4(this.px, Fp3.neg(this.py), this.pz);
+ }
+ double() {
+ const { a, b } = CURVE;
+ const b3 = Fp3.mul(b, _3n4);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO;
+ let t0 = Fp3.mul(X1, X1);
+ let t1 = Fp3.mul(Y1, Y1);
+ let t2 = Fp3.mul(Z1, Z1);
+ let t3 = Fp3.mul(X1, Y1);
+ t3 = Fp3.add(t3, t3);
+ Z3 = Fp3.mul(X1, Z1);
+ Z3 = Fp3.add(Z3, Z3);
+ X3 = Fp3.mul(a, Z3);
+ Y3 = Fp3.mul(b3, t2);
+ Y3 = Fp3.add(X3, Y3);
+ X3 = Fp3.sub(t1, Y3);
+ Y3 = Fp3.add(t1, Y3);
+ Y3 = Fp3.mul(X3, Y3);
+ X3 = Fp3.mul(t3, X3);
+ Z3 = Fp3.mul(b3, Z3);
+ t2 = Fp3.mul(a, t2);
+ t3 = Fp3.sub(t0, t2);
+ t3 = Fp3.mul(a, t3);
+ t3 = Fp3.add(t3, Z3);
+ Z3 = Fp3.add(t0, t0);
+ t0 = Fp3.add(Z3, t0);
+ t0 = Fp3.add(t0, t2);
+ t0 = Fp3.mul(t0, t3);
+ Y3 = Fp3.add(Y3, t0);
+ t2 = Fp3.mul(Y1, Z1);
+ t2 = Fp3.add(t2, t2);
+ t0 = Fp3.mul(t2, t3);
+ X3 = Fp3.sub(X3, t0);
+ Z3 = Fp3.mul(t2, t1);
+ Z3 = Fp3.add(Z3, Z3);
+ Z3 = Fp3.add(Z3, Z3);
+ return new Point4(X3, Y3, Z3);
+ }
+ add(other) {
+ assertPrjPoint(other);
+ const { px: X1, py: Y1, pz: Z1 } = this;
+ const { px: X2, py: Y2, pz: Z2 } = other;
+ let X3 = Fp3.ZERO, Y3 = Fp3.ZERO, Z3 = Fp3.ZERO;
+ const a = CURVE.a;
+ const b3 = Fp3.mul(CURVE.b, _3n4);
+ let t0 = Fp3.mul(X1, X2);
+ let t1 = Fp3.mul(Y1, Y2);
+ let t2 = Fp3.mul(Z1, Z2);
+ let t3 = Fp3.add(X1, Y1);
+ let t4 = Fp3.add(X2, Y2);
+ t3 = Fp3.mul(t3, t4);
+ t4 = Fp3.add(t0, t1);
+ t3 = Fp3.sub(t3, t4);
+ t4 = Fp3.add(X1, Z1);
+ let t5 = Fp3.add(X2, Z2);
+ t4 = Fp3.mul(t4, t5);
+ t5 = Fp3.add(t0, t2);
+ t4 = Fp3.sub(t4, t5);
+ t5 = Fp3.add(Y1, Z1);
+ X3 = Fp3.add(Y2, Z2);
+ t5 = Fp3.mul(t5, X3);
+ X3 = Fp3.add(t1, t2);
+ t5 = Fp3.sub(t5, X3);
+ Z3 = Fp3.mul(a, t4);
+ X3 = Fp3.mul(b3, t2);
+ Z3 = Fp3.add(X3, Z3);
+ X3 = Fp3.sub(t1, Z3);
+ Z3 = Fp3.add(t1, Z3);
+ Y3 = Fp3.mul(X3, Z3);
+ t1 = Fp3.add(t0, t0);
+ t1 = Fp3.add(t1, t0);
+ t2 = Fp3.mul(a, t2);
+ t4 = Fp3.mul(b3, t4);
+ t1 = Fp3.add(t1, t2);
+ t2 = Fp3.sub(t0, t2);
+ t2 = Fp3.mul(a, t2);
+ t4 = Fp3.add(t4, t2);
+ t0 = Fp3.mul(t1, t4);
+ Y3 = Fp3.add(Y3, t0);
+ t0 = Fp3.mul(t5, t4);
+ X3 = Fp3.mul(t3, X3);
+ X3 = Fp3.sub(X3, t0);
+ t0 = Fp3.mul(t3, t1);
+ Z3 = Fp3.mul(t5, Z3);
+ Z3 = Fp3.add(Z3, t0);
+ return new Point4(X3, Y3, Z3);
+ }
+ subtract(other) {
+ return this.add(other.negate());
+ }
+ is0() {
+ return this.equals(Point4.ZERO);
+ }
+ wNAF(n) {
+ return wnaf.wNAFCached(this, pointPrecomputes, n, (comp) => {
+ const toInv = Fp3.invertBatch(comp.map((p) => p.pz));
+ return comp.map((p, i2) => p.toAffine(toInv[i2])).map(Point4.fromAffine);
+ });
+ }
+ multiplyUnsafe(n) {
+ const I = Point4.ZERO;
+ if (n === _0n9)
+ return I;
+ assertGE(n);
+ if (n === _1n9)
+ return this;
+ const { endo } = CURVE;
+ if (!endo)
+ return wnaf.unsafeLadder(this, n);
+ let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
+ let k1p = I;
+ let k2p = I;
+ let d = this;
+ while (k1 > _0n9 || k2 > _0n9) {
+ if (k1 & _1n9)
+ k1p = k1p.add(d);
+ if (k2 & _1n9)
+ k2p = k2p.add(d);
+ d = d.double();
+ k1 >>= _1n9;
+ k2 >>= _1n9;
+ }
+ if (k1neg)
+ k1p = k1p.negate();
+ if (k2neg)
+ k2p = k2p.negate();
+ k2p = new Point4(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
+ return k1p.add(k2p);
+ }
+ multiply(scalar) {
+ assertGE(scalar);
+ let n = scalar;
+ let point, fake;
+ const { endo } = CURVE;
+ if (endo) {
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
+ let { p: k1p, f: f1p } = this.wNAF(k1);
+ let { p: k2p, f: f2p } = this.wNAF(k2);
+ k1p = wnaf.constTimeNegate(k1neg, k1p);
+ k2p = wnaf.constTimeNegate(k2neg, k2p);
+ k2p = new Point4(Fp3.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
+ point = k1p.add(k2p);
+ fake = f1p.add(f2p);
+ } else {
+ const { p, f: f2 } = this.wNAF(n);
+ point = p;
+ fake = f2;
+ }
+ return Point4.normalizeZ([point, fake])[0];
+ }
+ multiplyAndAddUnsafe(Q, a, b) {
+ const G = Point4.BASE;
+ const mul3 = (P, a2) => a2 === _0n9 || a2 === _1n9 || !P.equals(G) ? P.multiplyUnsafe(a2) : P.multiply(a2);
+ const sum = mul3(this, a).add(mul3(Q, b));
+ return sum.is0() ? void 0 : sum;
+ }
+ toAffine(iz) {
+ const { px: x, py: y, pz: z } = this;
+ const is0 = this.is0();
+ if (iz == null)
+ iz = is0 ? Fp3.ONE : Fp3.inv(z);
+ const ax = Fp3.mul(x, iz);
+ const ay = Fp3.mul(y, iz);
+ const zz = Fp3.mul(z, iz);
+ if (is0)
+ return { x: Fp3.ZERO, y: Fp3.ZERO };
+ if (!Fp3.eql(zz, Fp3.ONE))
+ throw new Error("invZ was invalid");
+ return { x: ax, y: ay };
+ }
+ isTorsionFree() {
+ const { h: cofactor, isTorsionFree } = CURVE;
+ if (cofactor === _1n9)
+ return true;
+ if (isTorsionFree)
+ return isTorsionFree(Point4, this);
+ throw new Error("isTorsionFree() has not been declared for the elliptic curve");
+ }
+ clearCofactor() {
+ const { h: cofactor, clearCofactor } = CURVE;
+ if (cofactor === _1n9)
+ return this;
+ if (clearCofactor)
+ return clearCofactor(Point4, this);
+ return this.multiplyUnsafe(CURVE.h);
+ }
+ toRawBytes(isCompressed = true) {
+ this.assertValidity();
+ return toBytes4(Point4, this, isCompressed);
+ }
+ toHex(isCompressed = true) {
+ return bytesToHex3(this.toRawBytes(isCompressed));
+ }
+ }
+ Point4.BASE = new Point4(CURVE.Gx, CURVE.Gy, Fp3.ONE);
+ Point4.ZERO = new Point4(Fp3.ZERO, Fp3.ONE, Fp3.ZERO);
+ const _bits = CURVE.nBitLength;
+ const wnaf = wNAF2(Point4, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
+ return {
+ CURVE,
+ ProjectivePoint: Point4,
+ normPrivateKeyToScalar,
+ weierstrassEquation,
+ isWithinCurveOrder
+ };
+ }
+ function validateOpts2(curve) {
+ const opts = validateBasic2(curve);
+ validateObject2(opts, {
+ hash: "hash",
+ hmac: "function",
+ randomBytes: "function"
+ }, {
+ bits2int: "function",
+ bits2int_modN: "function",
+ lowS: "boolean"
+ });
+ return Object.freeze({ lowS: true, ...opts });
+ }
+ function weierstrass2(curveDef) {
+ const CURVE = validateOpts2(curveDef);
+ const { Fp: Fp3, n: CURVE_ORDER } = CURVE;
+ const compressedLen = Fp3.BYTES + 1;
+ const uncompressedLen = 2 * Fp3.BYTES + 1;
+ function isValidFieldElement(num) {
+ return _0n9 < num && num < Fp3.ORDER;
+ }
+ function modN2(a) {
+ return mod2(a, CURVE_ORDER);
+ }
+ function invN(a) {
+ return invert2(a, CURVE_ORDER);
+ }
+ const { ProjectivePoint: Point4, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints2({
+ ...CURVE,
+ toBytes(c, point, isCompressed) {
+ const a = point.toAffine();
+ const x = Fp3.toBytes(a.x);
+ const cat = concatBytes4;
+ if (isCompressed) {
+ return cat(Uint8Array.from([point.hasEvenY() ? 2 : 3]), x);
+ } else {
+ return cat(Uint8Array.from([4]), x, Fp3.toBytes(a.y));
+ }
+ },
+ fromBytes(bytes4) {
+ const len = bytes4.length;
+ const head = bytes4[0];
+ const tail = bytes4.subarray(1);
+ if (len === compressedLen && (head === 2 || head === 3)) {
+ const x = bytesToNumberBE2(tail);
+ if (!isValidFieldElement(x))
+ throw new Error("Point is not on curve");
+ const y2 = weierstrassEquation(x);
+ let y = Fp3.sqrt(y2);
+ const isYOdd = (y & _1n9) === _1n9;
+ const isHeadOdd = (head & 1) === 1;
+ if (isHeadOdd !== isYOdd)
+ y = Fp3.neg(y);
+ return { x, y };
+ } else if (len === uncompressedLen && head === 4) {
+ const x = Fp3.fromBytes(tail.subarray(0, Fp3.BYTES));
+ const y = Fp3.fromBytes(tail.subarray(Fp3.BYTES, 2 * Fp3.BYTES));
+ return { x, y };
+ } else {
+ throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`);
+ }
+ }
+ });
+ const numToNByteStr = (num) => bytesToHex3(numberToBytesBE2(num, CURVE.nByteLength));
+ function isBiggerThanHalfOrder(number4) {
+ const HALF = CURVE_ORDER >> _1n9;
+ return number4 > HALF;
+ }
+ function normalizeS(s) {
+ return isBiggerThanHalfOrder(s) ? modN2(-s) : s;
+ }
+ const slcNum = (b, from, to) => bytesToNumberBE2(b.slice(from, to));
+ class Signature {
+ constructor(r, s, recovery) {
+ this.r = r;
+ this.s = s;
+ this.recovery = recovery;
+ this.assertValidity();
+ }
+ static fromCompact(hex2) {
+ const l = CURVE.nByteLength;
+ hex2 = ensureBytes2("compactSignature", hex2, l * 2);
+ return new Signature(slcNum(hex2, 0, l), slcNum(hex2, l, 2 * l));
+ }
+ static fromDER(hex2) {
+ const { r, s } = DER2.toSig(ensureBytes2("DER", hex2));
+ return new Signature(r, s);
+ }
+ assertValidity() {
+ if (!isWithinCurveOrder(this.r))
+ throw new Error("r must be 0 < r < CURVE.n");
+ if (!isWithinCurveOrder(this.s))
+ throw new Error("s must be 0 < s < CURVE.n");
+ }
+ addRecoveryBit(recovery) {
+ return new Signature(this.r, this.s, recovery);
+ }
+ recoverPublicKey(msgHash) {
+ const { r, s, recovery: rec } = this;
+ const h = bits2int_modN(ensureBytes2("msgHash", msgHash));
+ if (rec == null || ![0, 1, 2, 3].includes(rec))
+ throw new Error("recovery id invalid");
+ const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
+ if (radj >= Fp3.ORDER)
+ throw new Error("recovery id 2 or 3 invalid");
+ const prefix = (rec & 1) === 0 ? "02" : "03";
+ const R = Point4.fromHex(prefix + numToNByteStr(radj));
+ const ir = invN(radj);
+ const u1 = modN2(-h * ir);
+ const u2 = modN2(s * ir);
+ const Q = Point4.BASE.multiplyAndAddUnsafe(R, u1, u2);
+ if (!Q)
+ throw new Error("point at infinify");
+ Q.assertValidity();
+ return Q;
+ }
+ hasHighS() {
+ return isBiggerThanHalfOrder(this.s);
+ }
+ normalizeS() {
+ return this.hasHighS() ? new Signature(this.r, modN2(-this.s), this.recovery) : this;
+ }
+ toDERRawBytes() {
+ return hexToBytes3(this.toDERHex());
+ }
+ toDERHex() {
+ return DER2.hexFromSig({ r: this.r, s: this.s });
+ }
+ toCompactRawBytes() {
+ return hexToBytes3(this.toCompactHex());
+ }
+ toCompactHex() {
+ return numToNByteStr(this.r) + numToNByteStr(this.s);
+ }
+ }
+ const utils2 = {
+ isValidPrivateKey(privateKey) {
+ try {
+ normPrivateKeyToScalar(privateKey);
+ return true;
+ } catch (error) {
+ return false;
+ }
+ },
+ normPrivateKeyToScalar,
+ randomPrivateKey: () => {
+ const rand = CURVE.randomBytes(Fp3.BYTES + 8);
+ const num = hashToPrivateScalar(rand, CURVE_ORDER);
+ return numberToBytesBE2(num, CURVE.nByteLength);
+ },
+ precompute(windowSize = 8, point = Point4.BASE) {
+ point._setWindowSize(windowSize);
+ point.multiply(BigInt(3));
+ return point;
+ }
+ };
+ function getPublicKey2(privateKey, isCompressed = true) {
+ return Point4.fromPrivateKey(privateKey).toRawBytes(isCompressed);
+ }
+ function isProbPub(item) {
+ const arr = item instanceof Uint8Array;
+ const str = typeof item === "string";
+ const len = (arr || str) && item.length;
+ if (arr)
+ return len === compressedLen || len === uncompressedLen;
+ if (str)
+ return len === 2 * compressedLen || len === 2 * uncompressedLen;
+ if (item instanceof Point4)
+ return true;
+ return false;
+ }
+ function getSharedSecret(privateA, publicB, isCompressed = true) {
+ if (isProbPub(privateA))
+ throw new Error("first arg must be private key");
+ if (!isProbPub(publicB))
+ throw new Error("second arg must be public key");
+ const b = Point4.fromHex(publicB);
+ return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
+ }
+ const bits2int = CURVE.bits2int || function(bytes4) {
+ const num = bytesToNumberBE2(bytes4);
+ const delta = bytes4.length * 8 - CURVE.nBitLength;
+ return delta > 0 ? num >> BigInt(delta) : num;
+ };
+ const bits2int_modN = CURVE.bits2int_modN || function(bytes4) {
+ return modN2(bits2int(bytes4));
+ };
+ const ORDER_MASK = bitMask2(CURVE.nBitLength);
+ function int2octets(num) {
+ if (typeof num !== "bigint")
+ throw new Error("bigint expected");
+ if (!(_0n9 <= num && num < ORDER_MASK))
+ throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
+ return numberToBytesBE2(num, CURVE.nByteLength);
+ }
+ function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
+ if (["recovered", "canonical"].some((k) => k in opts))
+ throw new Error("sign() legacy options not supported");
+ const { hash: hash3, randomBytes: randomBytes3 } = CURVE;
+ let { lowS, prehash, extraEntropy: ent } = opts;
+ if (lowS == null)
+ lowS = true;
+ msgHash = ensureBytes2("msgHash", msgHash);
+ if (prehash)
+ msgHash = ensureBytes2("prehashed msgHash", hash3(msgHash));
+ const h1int = bits2int_modN(msgHash);
+ const d = normPrivateKeyToScalar(privateKey);
+ const seedArgs = [int2octets(d), int2octets(h1int)];
+ if (ent != null) {
+ const e = ent === true ? randomBytes3(Fp3.BYTES) : ent;
+ seedArgs.push(ensureBytes2("extraEntropy", e, Fp3.BYTES));
+ }
+ const seed = concatBytes4(...seedArgs);
+ const m = h1int;
+ function k2sig(kBytes) {
+ const k = bits2int(kBytes);
+ if (!isWithinCurveOrder(k))
+ return;
+ const ik = invN(k);
+ const q = Point4.BASE.multiply(k).toAffine();
+ const r = modN2(q.x);
+ if (r === _0n9)
+ return;
+ const s = modN2(ik * modN2(m + r * d));
+ if (s === _0n9)
+ return;
+ let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n9);
+ let normS = s;
+ if (lowS && isBiggerThanHalfOrder(s)) {
+ normS = normalizeS(s);
+ recovery ^= 1;
+ }
+ return new Signature(r, normS, recovery);
+ }
+ return { seed, k2sig };
+ }
+ const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
+ const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
+ function sign(msgHash, privKey, opts = defaultSigOpts) {
+ const { seed, k2sig } = prepSig(msgHash, privKey, opts);
+ const C = CURVE;
+ const drbg = createHmacDrbg2(C.hash.outputLen, C.nByteLength, C.hmac);
+ return drbg(seed, k2sig);
+ }
+ Point4.BASE._setWindowSize(8);
+ function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
+ const sg = signature;
+ msgHash = ensureBytes2("msgHash", msgHash);
+ publicKey = ensureBytes2("publicKey", publicKey);
+ if ("strict" in opts)
+ throw new Error("options.strict was renamed to lowS");
+ const { lowS, prehash } = opts;
+ let _sig = void 0;
+ let P;
+ try {
+ if (typeof sg === "string" || sg instanceof Uint8Array) {
+ try {
+ _sig = Signature.fromDER(sg);
+ } catch (derError) {
+ if (!(derError instanceof DER2.Err))
+ throw derError;
+ _sig = Signature.fromCompact(sg);
+ }
+ } else if (typeof sg === "object" && typeof sg.r === "bigint" && typeof sg.s === "bigint") {
+ const { r: r2, s: s2 } = sg;
+ _sig = new Signature(r2, s2);
+ } else {
+ throw new Error("PARSE");
+ }
+ P = Point4.fromHex(publicKey);
+ } catch (error) {
+ if (error.message === "PARSE")
+ throw new Error(`signature must be Signature instance, Uint8Array or hex string`);
+ return false;
+ }
+ if (lowS && _sig.hasHighS())
+ return false;
+ if (prehash)
+ msgHash = CURVE.hash(msgHash);
+ const { r, s } = _sig;
+ const h = bits2int_modN(msgHash);
+ const is = invN(s);
+ const u1 = modN2(h * is);
+ const u2 = modN2(r * is);
+ const R = Point4.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine();
+ if (!R)
+ return false;
+ const v = modN2(R.x);
+ return v === r;
+ }
+ return {
+ CURVE,
+ getPublicKey: getPublicKey2,
+ getSharedSecret,
+ sign,
+ verify,
+ ProjectivePoint: Point4,
+ Signature,
+ utils: utils2
+ };
+ }
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/_shortw_utils.js
+ function getHash2(hash3) {
+ return {
+ hash: hash3,
+ hmac: (key, ...msgs) => hmac2(hash3, key, concatBytes3(...msgs)),
+ randomBytes: randomBytes2
+ };
+ }
+ function createCurve2(curveDef, defHash) {
+ const create = (hash3) => weierstrass2({ ...curveDef, ...getHash2(hash3) });
+ return Object.freeze({ ...create(defHash), create });
+ }
+
+ // node_modules/@scure/bip32/node_modules/@noble/curves/esm/secp256k1.js
+ var secp256k1P2 = BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
+ var secp256k1N2 = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
+ var _1n10 = BigInt(1);
+ var _2n8 = BigInt(2);
+ var divNearest2 = (a, b) => (a + b / _2n8) / b;
+ function sqrtMod2(y) {
+ const P = secp256k1P2;
+ const _3n5 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
+ const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
+ const b2 = y * y * y % P;
+ const b3 = b2 * b2 * y % P;
+ const b6 = pow22(b3, _3n5, P) * b3 % P;
+ const b9 = pow22(b6, _3n5, P) * b3 % P;
+ const b11 = pow22(b9, _2n8, P) * b2 % P;
+ const b22 = pow22(b11, _11n, P) * b11 % P;
+ const b44 = pow22(b22, _22n, P) * b22 % P;
+ const b88 = pow22(b44, _44n, P) * b44 % P;
+ const b176 = pow22(b88, _88n, P) * b88 % P;
+ const b220 = pow22(b176, _44n, P) * b44 % P;
+ const b223 = pow22(b220, _3n5, P) * b3 % P;
+ const t1 = pow22(b223, _23n, P) * b22 % P;
+ const t2 = pow22(t1, _6n, P) * b2 % P;
+ const root = pow22(t2, _2n8, P);
+ if (!Fp2.eql(Fp2.sqr(root), y))
+ throw new Error("Cannot find square root");
+ return root;
+ }
+ var Fp2 = Field2(secp256k1P2, void 0, void 0, { sqrt: sqrtMod2 });
+ var secp256k12 = createCurve2({
+ a: BigInt(0),
+ b: BigInt(7),
+ Fp: Fp2,
+ n: secp256k1N2,
+ Gx: BigInt("55066263022277343669578718895168534326250603453777594175500187360389116729240"),
+ Gy: BigInt("32670510020758816978083085130507043184471273380659243275938904335757337482424"),
+ h: BigInt(1),
+ lowS: true,
+ endo: {
+ beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"),
+ splitScalar: (k) => {
+ const n = secp256k1N2;
+ const a1 = BigInt("0x3086d221a7d46bcde86c90e49284eb15");
+ const b1 = -_1n10 * BigInt("0xe4437ed6010e88286f547fa90abfe4c3");
+ const a2 = BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8");
+ const b2 = a1;
+ const POW_2_128 = BigInt("0x100000000000000000000000000000000");
+ const c1 = divNearest2(b2 * k, n);
+ const c2 = divNearest2(-b1 * k, n);
+ let k1 = mod2(k - c1 * a1 - c2 * a2, n);
+ let k2 = mod2(-c1 * b1 - c2 * b2, n);
+ const k1neg = k1 > POW_2_128;
+ const k2neg = k2 > POW_2_128;
+ if (k1neg)
+ k1 = n - k1;
+ if (k2neg)
+ k2 = n - k2;
+ if (k1 > POW_2_128 || k2 > POW_2_128) {
+ throw new Error("splitScalar: Endomorphism failed, k=" + k);
+ }
+ return { k1neg, k1, k2neg, k2 };
+ }
+ }
+ }, sha2562);
+ var _0n10 = BigInt(0);
+ var Point2 = secp256k12.ProjectivePoint;
+
+ // node_modules/@scure/bip32/lib/esm/index.js
+ var Point3 = secp256k12.ProjectivePoint;
+ var base58check2 = base58check(sha2562);
+ function bytesToNumber(bytes4) {
+ return BigInt(`0x${bytesToHex2(bytes4)}`);
+ }
+ function numberToBytes(num) {
+ return hexToBytes2(num.toString(16).padStart(64, "0"));
+ }
+ var MASTER_SECRET = utf8ToBytes3("Bitcoin seed");
+ var BITCOIN_VERSIONS = { private: 76066276, public: 76067358 };
+ var HARDENED_OFFSET = 2147483648;
+ var hash160 = (data) => ripemd160(sha2562(data));
+ var fromU32 = (data) => createView2(data).getUint32(0, false);
+ var toU32 = (n) => {
+ if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) {
+ throw new Error(`Invalid number=${n}. Should be from 0 to 2 ** 32 - 1`);
+ }
+ const buf = new Uint8Array(4);
+ createView2(buf).setUint32(0, n, false);
+ return buf;
+ };
+ var HDKey = class {
+ get fingerprint() {
+ if (!this.pubHash) {
+ throw new Error("No publicKey set!");
+ }
+ return fromU32(this.pubHash);
+ }
+ get identifier() {
+ return this.pubHash;
+ }
+ get pubKeyHash() {
+ return this.pubHash;
+ }
+ get privateKey() {
+ return this.privKeyBytes || null;
+ }
+ get publicKey() {
+ return this.pubKey || null;
+ }
+ get privateExtendedKey() {
+ const priv = this.privateKey;
+ if (!priv) {
+ throw new Error("No private key");
+ }
+ return base58check2.encode(this.serialize(this.versions.private, concatBytes3(new Uint8Array([0]), priv)));
+ }
+ get publicExtendedKey() {
+ if (!this.pubKey) {
+ throw new Error("No public key");
+ }
+ return base58check2.encode(this.serialize(this.versions.public, this.pubKey));
+ }
+ static fromMasterSeed(seed, versions = BITCOIN_VERSIONS) {
+ bytes2(seed);
+ if (8 * seed.length < 128 || 8 * seed.length > 512) {
+ throw new Error(`HDKey: wrong seed length=${seed.length}. Should be between 128 and 512 bits; 256 bits is advised)`);
+ }
+ const I = hmac2(sha512, MASTER_SECRET, seed);
+ return new HDKey({
+ versions,
+ chainCode: I.slice(32),
+ privateKey: I.slice(0, 32)
+ });
+ }
+ static fromExtendedKey(base58key, versions = BITCOIN_VERSIONS) {
+ const keyBuffer = base58check2.decode(base58key);
+ const keyView = createView2(keyBuffer);
+ const version = keyView.getUint32(0, false);
+ const opt = {
+ versions,
+ depth: keyBuffer[4],
+ parentFingerprint: keyView.getUint32(5, false),
+ index: keyView.getUint32(9, false),
+ chainCode: keyBuffer.slice(13, 45)
+ };
+ const key = keyBuffer.slice(45);
+ const isPriv = key[0] === 0;
+ if (version !== versions[isPriv ? "private" : "public"]) {
+ throw new Error("Version mismatch");
+ }
+ if (isPriv) {
+ return new HDKey({ ...opt, privateKey: key.slice(1) });
+ } else {
+ return new HDKey({ ...opt, publicKey: key });
+ }
+ }
+ static fromJSON(json) {
+ return HDKey.fromExtendedKey(json.xpriv);
+ }
+ constructor(opt) {
+ this.depth = 0;
+ this.index = 0;
+ this.chainCode = null;
+ this.parentFingerprint = 0;
+ if (!opt || typeof opt !== "object") {
+ throw new Error("HDKey.constructor must not be called directly");
+ }
+ this.versions = opt.versions || BITCOIN_VERSIONS;
+ this.depth = opt.depth || 0;
+ this.chainCode = opt.chainCode;
+ this.index = opt.index || 0;
+ this.parentFingerprint = opt.parentFingerprint || 0;
+ if (!this.depth) {
+ if (this.parentFingerprint || this.index) {
+ throw new Error("HDKey: zero depth with non-zero index/parent fingerprint");
+ }
+ }
+ if (opt.publicKey && opt.privateKey) {
+ throw new Error("HDKey: publicKey and privateKey at same time.");
+ }
+ if (opt.privateKey) {
+ if (!secp256k12.utils.isValidPrivateKey(opt.privateKey)) {
+ throw new Error("Invalid private key");
+ }
+ this.privKey = typeof opt.privateKey === "bigint" ? opt.privateKey : bytesToNumber(opt.privateKey);
+ this.privKeyBytes = numberToBytes(this.privKey);
+ this.pubKey = secp256k12.getPublicKey(opt.privateKey, true);
+ } else if (opt.publicKey) {
+ this.pubKey = Point3.fromHex(opt.publicKey).toRawBytes(true);
+ } else {
+ throw new Error("HDKey: no public or private key provided");
+ }
+ this.pubHash = hash160(this.pubKey);
+ }
+ derive(path) {
+ if (!/^[mM]'?/.test(path)) {
+ throw new Error('Path must start with "m" or "M"');
+ }
+ if (/^[mM]'?$/.test(path)) {
+ return this;
+ }
+ const parts = path.replace(/^[mM]'?\//, "").split("/");
+ let child = this;
+ for (const c of parts) {
+ const m = /^(\d+)('?)$/.exec(c);
+ if (!m || m.length !== 3) {
+ throw new Error(`Invalid child index: ${c}`);
+ }
+ let idx = +m[1];
+ if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) {
+ throw new Error("Invalid index");
+ }
+ if (m[2] === "'") {
+ idx += HARDENED_OFFSET;
+ }
+ child = child.deriveChild(idx);
+ }
+ return child;
+ }
+ deriveChild(index) {
+ if (!this.pubKey || !this.chainCode) {
+ throw new Error("No publicKey or chainCode set");
+ }
+ let data = toU32(index);
+ if (index >= HARDENED_OFFSET) {
+ const priv = this.privateKey;
+ if (!priv) {
+ throw new Error("Could not derive hardened child key");
+ }
+ data = concatBytes3(new Uint8Array([0]), priv, data);
+ } else {
+ data = concatBytes3(this.pubKey, data);
+ }
+ const I = hmac2(sha512, this.chainCode, data);
+ const childTweak = bytesToNumber(I.slice(0, 32));
+ const chainCode = I.slice(32);
+ if (!secp256k12.utils.isValidPrivateKey(childTweak)) {
+ throw new Error("Tweak bigger than curve order");
+ }
+ const opt = {
+ versions: this.versions,
+ chainCode,
+ depth: this.depth + 1,
+ parentFingerprint: this.fingerprint,
+ index
+ };
+ try {
+ if (this.privateKey) {
+ const added = mod2(this.privKey + childTweak, secp256k12.CURVE.n);
+ if (!secp256k12.utils.isValidPrivateKey(added)) {
+ throw new Error("The tweak was out of range or the resulted private key is invalid");
+ }
+ opt.privateKey = added;
+ } else {
+ const added = Point3.fromHex(this.pubKey).add(Point3.fromPrivateKey(childTweak));
+ if (added.equals(Point3.ZERO)) {
+ throw new Error("The tweak was equal to negative P, which made the result key invalid");
+ }
+ opt.publicKey = added.toRawBytes(true);
+ }
+ return new HDKey(opt);
+ } catch (err) {
+ return this.deriveChild(index + 1);
+ }
+ }
+ sign(hash3) {
+ if (!this.privateKey) {
+ throw new Error("No privateKey set!");
+ }
+ bytes2(hash3, 32);
+ return secp256k12.sign(hash3, this.privKey).toCompactRawBytes();
+ }
+ verify(hash3, signature) {
+ bytes2(hash3, 32);
+ bytes2(signature, 64);
+ if (!this.publicKey) {
+ throw new Error("No publicKey set!");
+ }
+ let sig;
+ try {
+ sig = secp256k12.Signature.fromCompact(signature);
+ } catch (error) {
+ return false;
+ }
+ return secp256k12.verify(sig, hash3, this.publicKey);
+ }
+ wipePrivateData() {
+ this.privKey = void 0;
+ if (this.privKeyBytes) {
+ this.privKeyBytes.fill(0);
+ this.privKeyBytes = void 0;
+ }
+ return this;
+ }
+ toJSON() {
+ return {
+ xpriv: this.privateExtendedKey,
+ xpub: this.publicExtendedKey
+ };
+ }
+ serialize(version, key) {
+ if (!this.chainCode) {
+ throw new Error("No chainCode set");
+ }
+ bytes2(key, 33);
+ return concatBytes3(toU32(version), new Uint8Array([this.depth]), toU32(this.parentFingerprint), toU32(this.index), this.chainCode, key);
+ }
+ };
+
+ // nip06.ts
+ var DERIVATION_PATH = `m/44'/1237'`;
+ function privateKeyFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
+ let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
+ let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey;
+ if (!privateKey)
+ throw new Error("could not derive private key");
+ return privateKey;
+ }
+ function accountFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
+ const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
+ const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`);
+ const publicKey = bytesToHex2(seed.publicKey.slice(1));
+ const privateKey = seed.privateKey;
+ if (!privateKey || !publicKey) {
+ throw new Error("could not derive key pair");
+ }
+ return { privateKey, publicKey };
+ }
+ function extendedKeysFromSeedWords(mnemonic, passphrase, extendedAccountIndex = 0) {
+ let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
+ let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`);
+ let privateExtendedKey = seed.privateExtendedKey;
+ let publicExtendedKey = seed.publicExtendedKey;
+ if (!privateExtendedKey && !publicExtendedKey)
+ throw new Error("could not derive extended key pair");
+ return { privateExtendedKey, publicExtendedKey };
+ }
+ function accountFromExtendedKey(base58key, accountIndex = 0) {
+ let extendedKey = HDKey.fromExtendedKey(base58key);
+ let version = base58key.slice(0, 4);
+ let child = extendedKey.deriveChild(0).deriveChild(accountIndex);
+ let publicKey = bytesToHex2(child.publicKey.slice(1));
+ if (!publicKey)
+ throw new Error("could not derive public key");
+ if (version === "xprv") {
+ let privateKey = child.privateKey;
+ if (!privateKey)
+ throw new Error("could not derive private key");
+ return { privateKey, publicKey };
+ }
+ return { publicKey };
+ }
+ function generateSeedWords() {
+ return generateMnemonic(wordlist);
+ }
+ function validateWords(words) {
+ return validateMnemonic(words, wordlist);
+ }
+
+ // nip10.ts
+ var nip10_exports = {};
+ __export(nip10_exports, {
+ parse: () => parse
+ });
+ function parse(event) {
+ const result = {
+ reply: void 0,
+ root: void 0,
+ mentions: [],
+ profiles: [],
+ quotes: []
+ };
+ let maybeParent;
+ let maybeRoot;
+ for (let i2 = event.tags.length - 1; i2 >= 0; i2--) {
+ const tag = event.tags[i2];
+ if (tag[0] === "e" && tag[1]) {
+ const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag;
+ const eventPointer = {
+ id: eTagEventId,
+ relays: eTagRelayUrl ? [eTagRelayUrl] : [],
+ author: eTagAuthor
+ };
+ if (eTagMarker === "root") {
+ result.root = eventPointer;
+ continue;
+ }
+ if (eTagMarker === "reply") {
+ result.reply = eventPointer;
+ continue;
+ }
+ if (eTagMarker === "mention") {
+ result.mentions.push(eventPointer);
+ continue;
+ }
+ if (!maybeParent) {
+ maybeParent = eventPointer;
+ } else {
+ maybeRoot = eventPointer;
+ }
+ result.mentions.push(eventPointer);
+ continue;
+ }
+ if (tag[0] === "q" && tag[1]) {
+ const [_, eTagEventId, eTagRelayUrl] = tag;
+ result.quotes.push({
+ id: eTagEventId,
+ relays: eTagRelayUrl ? [eTagRelayUrl] : []
+ });
+ }
+ if (tag[0] === "p" && tag[1]) {
+ result.profiles.push({
+ pubkey: tag[1],
+ relays: tag[2] ? [tag[2]] : []
+ });
+ continue;
+ }
+ }
+ if (!result.root) {
+ result.root = maybeRoot || maybeParent || result.reply;
+ }
+ if (!result.reply) {
+ result.reply = maybeParent || result.root;
+ }
+ ;
+ [result.reply, result.root].forEach((ref) => {
+ if (!ref)
+ return;
+ let idx = result.mentions.indexOf(ref);
+ if (idx !== -1) {
+ result.mentions.splice(idx, 1);
+ }
+ if (ref.author) {
+ let author = result.profiles.find((p) => p.pubkey === ref.author);
+ if (author && author.relays) {
+ if (!ref.relays) {
+ ref.relays = [];
+ }
+ author.relays.forEach((url) => {
+ if (ref.relays?.indexOf(url) === -1)
+ ref.relays.push(url);
+ });
+ author.relays = ref.relays;
+ }
+ }
+ });
+ result.mentions.forEach((ref) => {
+ if (ref.author) {
+ let author = result.profiles.find((p) => p.pubkey === ref.author);
+ if (author && author.relays) {
+ if (!ref.relays) {
+ ref.relays = [];
+ }
+ author.relays.forEach((url) => {
+ if (ref.relays.indexOf(url) === -1)
+ ref.relays.push(url);
+ });
+ author.relays = ref.relays;
+ }
+ }
+ });
+ return result;
+ }
+
+ // nip11.ts
+ var nip11_exports = {};
+ __export(nip11_exports, {
+ fetchRelayInformation: () => fetchRelayInformation,
+ useFetchImplementation: () => useFetchImplementation2
+ });
+ var _fetch2;
+ try {
+ _fetch2 = fetch;
+ } catch {
+ }
+ function useFetchImplementation2(fetchImplementation) {
+ _fetch2 = fetchImplementation;
+ }
+ async function fetchRelayInformation(url) {
+ return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
+ headers: { Accept: "application/nostr+json" }
+ })).json();
+ }
+
+ // nip13.ts
+ var nip13_exports = {};
+ __export(nip13_exports, {
+ fastEventHash: () => fastEventHash,
+ getPow: () => getPow,
+ minePow: () => minePow
+ });
+ function getPow(hex2) {
+ let count = 0;
+ for (let i2 = 0; i2 < 64; i2 += 8) {
+ const nibble = parseInt(hex2.substring(i2, i2 + 8), 16);
+ if (nibble === 0) {
+ count += 32;
+ } else {
+ count += Math.clz32(nibble);
+ break;
+ }
+ }
+ return count;
+ }
+ function minePow(unsigned, difficulty) {
+ let count = 0;
+ const event = unsigned;
+ const tag = ["nonce", count.toString(), difficulty.toString()];
+ event.tags.push(tag);
+ while (true) {
+ const now2 = Math.floor(new Date().getTime() / 1e3);
+ if (now2 !== event.created_at) {
+ count = 0;
+ event.created_at = now2;
+ }
+ tag[1] = (++count).toString();
+ event.id = fastEventHash(event);
+ if (getPow(event.id) >= difficulty) {
+ break;
+ }
+ }
+ return event;
+ }
+ function fastEventHash(evt) {
+ return bytesToHex2(
+ sha2562(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
+ );
+ }
+
+ // nip17.ts
+ var nip17_exports = {};
+ __export(nip17_exports, {
+ unwrapEvent: () => unwrapEvent2,
+ unwrapManyEvents: () => unwrapManyEvents2,
+ wrapEvent: () => wrapEvent2,
+ wrapManyEvents: () => wrapManyEvents2
+ });
+
+ // nip59.ts
+ var nip59_exports = {};
+ __export(nip59_exports, {
+ createRumor: () => createRumor,
+ createSeal: () => createSeal,
+ createWrap: () => createWrap,
+ unwrapEvent: () => unwrapEvent,
+ unwrapManyEvents: () => unwrapManyEvents,
+ wrapEvent: () => wrapEvent,
+ wrapManyEvents: () => wrapManyEvents
+ });
+
+ // nip44.ts
+ var nip44_exports = {};
+ __export(nip44_exports, {
+ decrypt: () => decrypt3,
+ encrypt: () => encrypt3,
+ getConversationKey: () => getConversationKey,
+ v2: () => v2
+ });
+
+ // node_modules/@noble/ciphers/esm/_poly1305.js
+ var u8to16 = (a, i2) => a[i2++] & 255 | (a[i2++] & 255) << 8;
+ var Poly1305 = class {
+ constructor(key) {
+ this.blockLen = 16;
+ this.outputLen = 16;
+ this.buffer = new Uint8Array(16);
+ this.r = new Uint16Array(10);
+ this.h = new Uint16Array(10);
+ this.pad = new Uint16Array(8);
+ this.pos = 0;
+ this.finished = false;
+ key = toBytes3(key);
+ bytes3(key, 32);
+ const t0 = u8to16(key, 0);
+ const t1 = u8to16(key, 2);
+ const t2 = u8to16(key, 4);
+ const t3 = u8to16(key, 6);
+ const t4 = u8to16(key, 8);
+ const t5 = u8to16(key, 10);
+ const t6 = u8to16(key, 12);
+ const t7 = u8to16(key, 14);
+ this.r[0] = t0 & 8191;
+ this.r[1] = (t0 >>> 13 | t1 << 3) & 8191;
+ this.r[2] = (t1 >>> 10 | t2 << 6) & 7939;
+ this.r[3] = (t2 >>> 7 | t3 << 9) & 8191;
+ this.r[4] = (t3 >>> 4 | t4 << 12) & 255;
+ this.r[5] = t4 >>> 1 & 8190;
+ this.r[6] = (t4 >>> 14 | t5 << 2) & 8191;
+ this.r[7] = (t5 >>> 11 | t6 << 5) & 8065;
+ this.r[8] = (t6 >>> 8 | t7 << 8) & 8191;
+ this.r[9] = t7 >>> 5 & 127;
+ for (let i2 = 0; i2 < 8; i2++)
+ this.pad[i2] = u8to16(key, 16 + 2 * i2);
+ }
+ process(data, offset, isLast = false) {
+ const hibit = isLast ? 0 : 1 << 11;
+ const { h, r } = this;
+ const r0 = r[0];
+ const r1 = r[1];
+ const r2 = r[2];
+ const r3 = r[3];
+ const r4 = r[4];
+ const r5 = r[5];
+ const r6 = r[6];
+ const r7 = r[7];
+ const r8 = r[8];
+ const r9 = r[9];
+ const t0 = u8to16(data, offset + 0);
+ const t1 = u8to16(data, offset + 2);
+ const t2 = u8to16(data, offset + 4);
+ const t3 = u8to16(data, offset + 6);
+ const t4 = u8to16(data, offset + 8);
+ const t5 = u8to16(data, offset + 10);
+ const t6 = u8to16(data, offset + 12);
+ const t7 = u8to16(data, offset + 14);
+ let h0 = h[0] + (t0 & 8191);
+ let h1 = h[1] + ((t0 >>> 13 | t1 << 3) & 8191);
+ let h2 = h[2] + ((t1 >>> 10 | t2 << 6) & 8191);
+ let h3 = h[3] + ((t2 >>> 7 | t3 << 9) & 8191);
+ let h4 = h[4] + ((t3 >>> 4 | t4 << 12) & 8191);
+ let h5 = h[5] + (t4 >>> 1 & 8191);
+ let h6 = h[6] + ((t4 >>> 14 | t5 << 2) & 8191);
+ let h7 = h[7] + ((t5 >>> 11 | t6 << 5) & 8191);
+ let h8 = h[8] + ((t6 >>> 8 | t7 << 8) & 8191);
+ let h9 = h[9] + (t7 >>> 5 | hibit);
+ let c = 0;
+ let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
+ c = d0 >>> 13;
+ d0 &= 8191;
+ d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
+ c += d0 >>> 13;
+ d0 &= 8191;
+ let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
+ c = d1 >>> 13;
+ d1 &= 8191;
+ d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
+ c += d1 >>> 13;
+ d1 &= 8191;
+ let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
+ c = d2 >>> 13;
+ d2 &= 8191;
+ d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
+ c += d2 >>> 13;
+ d2 &= 8191;
+ let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
+ c = d3 >>> 13;
+ d3 &= 8191;
+ d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
+ c += d3 >>> 13;
+ d3 &= 8191;
+ let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
+ c = d4 >>> 13;
+ d4 &= 8191;
+ d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
+ c += d4 >>> 13;
+ d4 &= 8191;
+ let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
+ c = d5 >>> 13;
+ d5 &= 8191;
+ d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
+ c += d5 >>> 13;
+ d5 &= 8191;
+ let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
+ c = d6 >>> 13;
+ d6 &= 8191;
+ d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
+ c += d6 >>> 13;
+ d6 &= 8191;
+ let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
+ c = d7 >>> 13;
+ d7 &= 8191;
+ d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
+ c += d7 >>> 13;
+ d7 &= 8191;
+ let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
+ c = d8 >>> 13;
+ d8 &= 8191;
+ d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
+ c += d8 >>> 13;
+ d8 &= 8191;
+ let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
+ c = d9 >>> 13;
+ d9 &= 8191;
+ d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
+ c += d9 >>> 13;
+ d9 &= 8191;
+ c = (c << 2) + c | 0;
+ c = c + d0 | 0;
+ d0 = c & 8191;
+ c = c >>> 13;
+ d1 += c;
+ h[0] = d0;
+ h[1] = d1;
+ h[2] = d2;
+ h[3] = d3;
+ h[4] = d4;
+ h[5] = d5;
+ h[6] = d6;
+ h[7] = d7;
+ h[8] = d8;
+ h[9] = d9;
+ }
+ finalize() {
+ const { h, pad: pad2 } = this;
+ const g = new Uint16Array(10);
+ let c = h[1] >>> 13;
+ h[1] &= 8191;
+ for (let i2 = 2; i2 < 10; i2++) {
+ h[i2] += c;
+ c = h[i2] >>> 13;
+ h[i2] &= 8191;
+ }
+ h[0] += c * 5;
+ c = h[0] >>> 13;
+ h[0] &= 8191;
+ h[1] += c;
+ c = h[1] >>> 13;
+ h[1] &= 8191;
+ h[2] += c;
+ g[0] = h[0] + 5;
+ c = g[0] >>> 13;
+ g[0] &= 8191;
+ for (let i2 = 1; i2 < 10; i2++) {
+ g[i2] = h[i2] + c;
+ c = g[i2] >>> 13;
+ g[i2] &= 8191;
+ }
+ g[9] -= 1 << 13;
+ let mask = (c ^ 1) - 1;
+ for (let i2 = 0; i2 < 10; i2++)
+ g[i2] &= mask;
+ mask = ~mask;
+ for (let i2 = 0; i2 < 10; i2++)
+ h[i2] = h[i2] & mask | g[i2];
+ h[0] = (h[0] | h[1] << 13) & 65535;
+ h[1] = (h[1] >>> 3 | h[2] << 10) & 65535;
+ h[2] = (h[2] >>> 6 | h[3] << 7) & 65535;
+ h[3] = (h[3] >>> 9 | h[4] << 4) & 65535;
+ h[4] = (h[4] >>> 12 | h[5] << 1 | h[6] << 14) & 65535;
+ h[5] = (h[6] >>> 2 | h[7] << 11) & 65535;
+ h[6] = (h[7] >>> 5 | h[8] << 8) & 65535;
+ h[7] = (h[8] >>> 8 | h[9] << 5) & 65535;
+ let f2 = h[0] + pad2[0];
+ h[0] = f2 & 65535;
+ for (let i2 = 1; i2 < 8; i2++) {
+ f2 = (h[i2] + pad2[i2] | 0) + (f2 >>> 16) | 0;
+ h[i2] = f2 & 65535;
+ }
+ }
+ update(data) {
+ exists3(this);
+ const { buffer, blockLen } = this;
+ data = toBytes3(data);
+ const len = data.length;
+ for (let pos = 0; pos < len; ) {
+ const take = Math.min(blockLen - this.pos, len - pos);
+ if (take === blockLen) {
+ for (; blockLen <= len - pos; pos += blockLen)
+ this.process(data, pos);
+ continue;
+ }
+ buffer.set(data.subarray(pos, pos + take), this.pos);
+ this.pos += take;
+ pos += take;
+ if (this.pos === blockLen) {
+ this.process(buffer, 0, false);
+ this.pos = 0;
+ }
+ }
+ return this;
+ }
+ destroy() {
+ this.h.fill(0);
+ this.r.fill(0);
+ this.buffer.fill(0);
+ this.pad.fill(0);
+ }
+ digestInto(out) {
+ exists3(this);
+ output3(out, this);
+ this.finished = true;
+ const { buffer, h } = this;
+ let { pos } = this;
+ if (pos) {
+ buffer[pos++] = 1;
+ for (; pos < 16; pos++)
+ buffer[pos] = 0;
+ this.process(buffer, 0, true);
+ }
+ this.finalize();
+ let opos = 0;
+ for (let i2 = 0; i2 < 8; i2++) {
+ out[opos++] = h[i2] >>> 0;
+ out[opos++] = h[i2] >>> 8;
+ }
+ return out;
+ }
+ digest() {
+ const { buffer, outputLen } = this;
+ this.digestInto(buffer);
+ const res = buffer.slice(0, outputLen);
+ this.destroy();
+ return res;
+ }
+ };
+ function wrapConstructorWithKey2(hashCons) {
+ const hashC = (msg, key) => hashCons(key).update(toBytes3(msg)).digest();
+ const tmp = hashCons(new Uint8Array(32));
+ hashC.outputLen = tmp.outputLen;
+ hashC.blockLen = tmp.blockLen;
+ hashC.create = (key) => hashCons(key);
+ return hashC;
+ }
+ var poly1305 = wrapConstructorWithKey2((key) => new Poly1305(key));
+
+ // node_modules/@noble/ciphers/esm/_arx.js
+ var _utf8ToBytes = (str) => Uint8Array.from(str.split("").map((c) => c.charCodeAt(0)));
+ var sigma16 = _utf8ToBytes("expand 16-byte k");
+ var sigma32 = _utf8ToBytes("expand 32-byte k");
+ var sigma16_32 = u32(sigma16);
+ var sigma32_32 = u32(sigma32);
+ var sigma = sigma32_32.slice();
+ function rotl2(a, b) {
+ return a << b | a >>> 32 - b;
+ }
+ function isAligned32(b) {
+ return b.byteOffset % 4 === 0;
+ }
+ var BLOCK_LEN = 64;
+ var BLOCK_LEN32 = 16;
+ var MAX_COUNTER = 2 ** 32 - 1;
+ var U32_EMPTY = new Uint32Array();
+ function runCipher(core, sigma2, key, nonce, data, output4, counter, rounds) {
+ const len = data.length;
+ const block = new Uint8Array(BLOCK_LEN);
+ const b32 = u32(block);
+ const isAligned = isAligned32(data) && isAligned32(output4);
+ const d32 = isAligned ? u32(data) : U32_EMPTY;
+ const o32 = isAligned ? u32(output4) : U32_EMPTY;
+ for (let pos = 0; pos < len; counter++) {
+ core(sigma2, key, nonce, b32, counter, rounds);
+ if (counter >= MAX_COUNTER)
+ throw new Error("arx: counter overflow");
+ const take = Math.min(BLOCK_LEN, len - pos);
+ if (isAligned && take === BLOCK_LEN) {
+ const pos32 = pos / 4;
+ if (pos % 4 !== 0)
+ throw new Error("arx: invalid block position");
+ for (let j = 0, posj; j < BLOCK_LEN32; j++) {
+ posj = pos32 + j;
+ o32[posj] = d32[posj] ^ b32[j];
+ }
+ pos += BLOCK_LEN;
+ continue;
+ }
+ for (let j = 0, posj; j < take; j++) {
+ posj = pos + j;
+ output4[posj] = data[posj] ^ block[j];
+ }
+ pos += take;
+ }
+ }
+ function createCipher(core, opts) {
+ const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts2({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
+ if (typeof core !== "function")
+ throw new Error("core must be a function");
+ number3(counterLength);
+ number3(rounds);
+ bool2(counterRight);
+ bool2(allowShortKeys);
+ return (key, nonce, data, output4, counter = 0) => {
+ bytes3(key);
+ bytes3(nonce);
+ bytes3(data);
+ const len = data.length;
+ if (!output4)
+ output4 = new Uint8Array(len);
+ bytes3(output4);
+ number3(counter);
+ if (counter < 0 || counter >= MAX_COUNTER)
+ throw new Error("arx: counter overflow");
+ if (output4.length < len)
+ throw new Error(`arx: output (${output4.length}) is shorter than data (${len})`);
+ const toClean = [];
+ let l = key.length, k, sigma2;
+ if (l === 32) {
+ k = key.slice();
+ toClean.push(k);
+ sigma2 = sigma32_32;
+ } else if (l === 16 && allowShortKeys) {
+ k = new Uint8Array(32);
+ k.set(key);
+ k.set(key, 16);
+ sigma2 = sigma16_32;
+ toClean.push(k);
+ } else {
+ throw new Error(`arx: invalid 32-byte key, got length=${l}`);
+ }
+ if (!isAligned32(nonce)) {
+ nonce = nonce.slice();
+ toClean.push(nonce);
+ }
+ const k32 = u32(k);
+ if (extendNonceFn) {
+ if (nonce.length !== 24)
+ throw new Error(`arx: extended nonce must be 24 bytes`);
+ extendNonceFn(sigma2, k32, u32(nonce.subarray(0, 16)), k32);
+ nonce = nonce.subarray(16);
+ }
+ const nonceNcLen = 16 - counterLength;
+ if (nonceNcLen !== nonce.length)
+ throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);
+ if (nonceNcLen !== 12) {
+ const nc = new Uint8Array(12);
+ nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
+ nonce = nc;
+ toClean.push(nonce);
+ }
+ const n32 = u32(nonce);
+ runCipher(core, sigma2, k32, n32, data, output4, counter, rounds);
+ while (toClean.length > 0)
+ toClean.pop().fill(0);
+ return output4;
+ };
+ }
+
+ // node_modules/@noble/ciphers/esm/chacha.js
+ function chachaCore(s, k, n, out, cnt, rounds = 20) {
+ let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2];
+ let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
+ for (let r = 0; r < rounds; r += 2) {
+ x00 = x00 + x04 | 0;
+ x12 = rotl2(x12 ^ x00, 16);
+ x08 = x08 + x12 | 0;
+ x04 = rotl2(x04 ^ x08, 12);
+ x00 = x00 + x04 | 0;
+ x12 = rotl2(x12 ^ x00, 8);
+ x08 = x08 + x12 | 0;
+ x04 = rotl2(x04 ^ x08, 7);
+ x01 = x01 + x05 | 0;
+ x13 = rotl2(x13 ^ x01, 16);
+ x09 = x09 + x13 | 0;
+ x05 = rotl2(x05 ^ x09, 12);
+ x01 = x01 + x05 | 0;
+ x13 = rotl2(x13 ^ x01, 8);
+ x09 = x09 + x13 | 0;
+ x05 = rotl2(x05 ^ x09, 7);
+ x02 = x02 + x06 | 0;
+ x14 = rotl2(x14 ^ x02, 16);
+ x10 = x10 + x14 | 0;
+ x06 = rotl2(x06 ^ x10, 12);
+ x02 = x02 + x06 | 0;
+ x14 = rotl2(x14 ^ x02, 8);
+ x10 = x10 + x14 | 0;
+ x06 = rotl2(x06 ^ x10, 7);
+ x03 = x03 + x07 | 0;
+ x15 = rotl2(x15 ^ x03, 16);
+ x11 = x11 + x15 | 0;
+ x07 = rotl2(x07 ^ x11, 12);
+ x03 = x03 + x07 | 0;
+ x15 = rotl2(x15 ^ x03, 8);
+ x11 = x11 + x15 | 0;
+ x07 = rotl2(x07 ^ x11, 7);
+ x00 = x00 + x05 | 0;
+ x15 = rotl2(x15 ^ x00, 16);
+ x10 = x10 + x15 | 0;
+ x05 = rotl2(x05 ^ x10, 12);
+ x00 = x00 + x05 | 0;
+ x15 = rotl2(x15 ^ x00, 8);
+ x10 = x10 + x15 | 0;
+ x05 = rotl2(x05 ^ x10, 7);
+ x01 = x01 + x06 | 0;
+ x12 = rotl2(x12 ^ x01, 16);
+ x11 = x11 + x12 | 0;
+ x06 = rotl2(x06 ^ x11, 12);
+ x01 = x01 + x06 | 0;
+ x12 = rotl2(x12 ^ x01, 8);
+ x11 = x11 + x12 | 0;
+ x06 = rotl2(x06 ^ x11, 7);
+ x02 = x02 + x07 | 0;
+ x13 = rotl2(x13 ^ x02, 16);
+ x08 = x08 + x13 | 0;
+ x07 = rotl2(x07 ^ x08, 12);
+ x02 = x02 + x07 | 0;
+ x13 = rotl2(x13 ^ x02, 8);
+ x08 = x08 + x13 | 0;
+ x07 = rotl2(x07 ^ x08, 7);
+ x03 = x03 + x04 | 0;
+ x14 = rotl2(x14 ^ x03, 16);
+ x09 = x09 + x14 | 0;
+ x04 = rotl2(x04 ^ x09, 12);
+ x03 = x03 + x04 | 0;
+ x14 = rotl2(x14 ^ x03, 8);
+ x09 = x09 + x14 | 0;
+ x04 = rotl2(x04 ^ x09, 7);
+ }
+ let oi = 0;
+ out[oi++] = y00 + x00 | 0;
+ out[oi++] = y01 + x01 | 0;
+ out[oi++] = y02 + x02 | 0;
+ out[oi++] = y03 + x03 | 0;
+ out[oi++] = y04 + x04 | 0;
+ out[oi++] = y05 + x05 | 0;
+ out[oi++] = y06 + x06 | 0;
+ out[oi++] = y07 + x07 | 0;
+ out[oi++] = y08 + x08 | 0;
+ out[oi++] = y09 + x09 | 0;
+ out[oi++] = y10 + x10 | 0;
+ out[oi++] = y11 + x11 | 0;
+ out[oi++] = y12 + x12 | 0;
+ out[oi++] = y13 + x13 | 0;
+ out[oi++] = y14 + x14 | 0;
+ out[oi++] = y15 + x15 | 0;
+ }
+ function hchacha(s, k, i2, o32) {
+ let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i2[0], x13 = i2[1], x14 = i2[2], x15 = i2[3];
+ for (let r = 0; r < 20; r += 2) {
+ x00 = x00 + x04 | 0;
+ x12 = rotl2(x12 ^ x00, 16);
+ x08 = x08 + x12 | 0;
+ x04 = rotl2(x04 ^ x08, 12);
+ x00 = x00 + x04 | 0;
+ x12 = rotl2(x12 ^ x00, 8);
+ x08 = x08 + x12 | 0;
+ x04 = rotl2(x04 ^ x08, 7);
+ x01 = x01 + x05 | 0;
+ x13 = rotl2(x13 ^ x01, 16);
+ x09 = x09 + x13 | 0;
+ x05 = rotl2(x05 ^ x09, 12);
+ x01 = x01 + x05 | 0;
+ x13 = rotl2(x13 ^ x01, 8);
+ x09 = x09 + x13 | 0;
+ x05 = rotl2(x05 ^ x09, 7);
+ x02 = x02 + x06 | 0;
+ x14 = rotl2(x14 ^ x02, 16);
+ x10 = x10 + x14 | 0;
+ x06 = rotl2(x06 ^ x10, 12);
+ x02 = x02 + x06 | 0;
+ x14 = rotl2(x14 ^ x02, 8);
+ x10 = x10 + x14 | 0;
+ x06 = rotl2(x06 ^ x10, 7);
+ x03 = x03 + x07 | 0;
+ x15 = rotl2(x15 ^ x03, 16);
+ x11 = x11 + x15 | 0;
+ x07 = rotl2(x07 ^ x11, 12);
+ x03 = x03 + x07 | 0;
+ x15 = rotl2(x15 ^ x03, 8);
+ x11 = x11 + x15 | 0;
+ x07 = rotl2(x07 ^ x11, 7);
+ x00 = x00 + x05 | 0;
+ x15 = rotl2(x15 ^ x00, 16);
+ x10 = x10 + x15 | 0;
+ x05 = rotl2(x05 ^ x10, 12);
+ x00 = x00 + x05 | 0;
+ x15 = rotl2(x15 ^ x00, 8);
+ x10 = x10 + x15 | 0;
+ x05 = rotl2(x05 ^ x10, 7);
+ x01 = x01 + x06 | 0;
+ x12 = rotl2(x12 ^ x01, 16);
+ x11 = x11 + x12 | 0;
+ x06 = rotl2(x06 ^ x11, 12);
+ x01 = x01 + x06 | 0;
+ x12 = rotl2(x12 ^ x01, 8);
+ x11 = x11 + x12 | 0;
+ x06 = rotl2(x06 ^ x11, 7);
+ x02 = x02 + x07 | 0;
+ x13 = rotl2(x13 ^ x02, 16);
+ x08 = x08 + x13 | 0;
+ x07 = rotl2(x07 ^ x08, 12);
+ x02 = x02 + x07 | 0;
+ x13 = rotl2(x13 ^ x02, 8);
+ x08 = x08 + x13 | 0;
+ x07 = rotl2(x07 ^ x08, 7);
+ x03 = x03 + x04 | 0;
+ x14 = rotl2(x14 ^ x03, 16);
+ x09 = x09 + x14 | 0;
+ x04 = rotl2(x04 ^ x09, 12);
+ x03 = x03 + x04 | 0;
+ x14 = rotl2(x14 ^ x03, 8);
+ x09 = x09 + x14 | 0;
+ x04 = rotl2(x04 ^ x09, 7);
+ }
+ let oi = 0;
+ o32[oi++] = x00;
+ o32[oi++] = x01;
+ o32[oi++] = x02;
+ o32[oi++] = x03;
+ o32[oi++] = x12;
+ o32[oi++] = x13;
+ o32[oi++] = x14;
+ o32[oi++] = x15;
+ }
+ var chacha20 = /* @__PURE__ */ createCipher(chachaCore, {
+ counterRight: false,
+ counterLength: 4,
+ allowShortKeys: false
+ });
+ var xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
+ counterRight: false,
+ counterLength: 8,
+ extendNonceFn: hchacha,
+ allowShortKeys: false
+ });
+ var ZEROS162 = /* @__PURE__ */ new Uint8Array(16);
+ var updatePadded = (h, msg) => {
+ h.update(msg);
+ const left = msg.length % 16;
+ if (left)
+ h.update(ZEROS162.subarray(left));
+ };
+ var ZEROS322 = /* @__PURE__ */ new Uint8Array(32);
+ function computeTag2(fn, key, nonce, data, AAD) {
+ const authKey = fn(key, nonce, ZEROS322);
+ const h = poly1305.create(authKey);
+ if (AAD)
+ updatePadded(h, AAD);
+ updatePadded(h, data);
+ const num = new Uint8Array(16);
+ const view = createView3(num);
+ setBigUint643(view, 0, BigInt(AAD ? AAD.length : 0), true);
+ setBigUint643(view, 8, BigInt(data.length), true);
+ h.update(num);
+ const res = h.digest();
+ authKey.fill(0);
+ return res;
+ }
+ var _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
+ const tagLength = 16;
+ bytes3(key, 32);
+ bytes3(nonce);
+ return {
+ encrypt: (plaintext, output4) => {
+ const plength = plaintext.length;
+ const clength = plength + tagLength;
+ if (output4) {
+ bytes3(output4, clength);
+ } else {
+ output4 = new Uint8Array(clength);
+ }
+ xorStream(key, nonce, plaintext, output4, 1);
+ const tag = computeTag2(xorStream, key, nonce, output4.subarray(0, -tagLength), AAD);
+ output4.set(tag, plength);
+ return output4;
+ },
+ decrypt: (ciphertext, output4) => {
+ const clength = ciphertext.length;
+ const plength = clength - tagLength;
+ if (clength < tagLength)
+ throw new Error(`encrypted data must be at least ${tagLength} bytes`);
+ if (output4) {
+ bytes3(output4, plength);
+ } else {
+ output4 = new Uint8Array(plength);
+ }
+ const data = ciphertext.subarray(0, -tagLength);
+ const passedTag = ciphertext.subarray(-tagLength);
+ const tag = computeTag2(xorStream, key, nonce, data, AAD);
+ if (!equalBytes2(passedTag, tag))
+ throw new Error("invalid tag");
+ xorStream(key, nonce, data, output4, 1);
+ return output4;
+ }
+ };
+ };
+ var chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20));
+ var xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
+
+ // node_modules/@noble/hashes/esm/hkdf.js
+ function extract(hash3, ikm, salt2) {
+ assert_default.hash(hash3);
+ if (salt2 === void 0)
+ salt2 = new Uint8Array(hash3.outputLen);
+ return hmac2(hash3, toBytes2(salt2), toBytes2(ikm));
+ }
+ var HKDF_COUNTER = new Uint8Array([0]);
+ var EMPTY_BUFFER = new Uint8Array();
+ function expand(hash3, prk, info, length = 32) {
+ assert_default.hash(hash3);
+ assert_default.number(length);
+ if (length > 255 * hash3.outputLen)
+ throw new Error("Length should be <= 255*HashLen");
+ const blocks = Math.ceil(length / hash3.outputLen);
+ if (info === void 0)
+ info = EMPTY_BUFFER;
+ const okm = new Uint8Array(blocks * hash3.outputLen);
+ const HMAC3 = hmac2.create(hash3, prk);
+ const HMACTmp = HMAC3._cloneInto();
+ const T = new Uint8Array(HMAC3.outputLen);
+ for (let counter = 0; counter < blocks; counter++) {
+ HKDF_COUNTER[0] = counter + 1;
+ HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T).update(info).update(HKDF_COUNTER).digestInto(T);
+ okm.set(T, hash3.outputLen * counter);
+ HMAC3._cloneInto(HMACTmp);
+ }
+ HMAC3.destroy();
+ HMACTmp.destroy();
+ T.fill(0);
+ HKDF_COUNTER.fill(0);
+ return okm.slice(0, length);
+ }
+
+ // nip44.ts
+ var minPlaintextSize = 1;
+ var maxPlaintextSize = 65535;
+ function getConversationKey(privkeyA, pubkeyB) {
+ const sharedX = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
+ return extract(sha2562, sharedX, "nip44-v2");
+ }
+ function getMessageKeys(conversationKey, nonce) {
+ const keys = expand(sha2562, conversationKey, nonce, 76);
+ return {
+ chacha_key: keys.subarray(0, 32),
+ chacha_nonce: keys.subarray(32, 44),
+ hmac_key: keys.subarray(44, 76)
+ };
+ }
+ function calcPaddedLen(len) {
+ if (!Number.isSafeInteger(len) || len < 1)
+ throw new Error("expected positive integer");
+ if (len <= 32)
+ return 32;
+ const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
+ const chunk = nextPower <= 256 ? 32 : nextPower / 8;
+ return chunk * (Math.floor((len - 1) / chunk) + 1);
+ }
+ function writeU16BE(num) {
+ if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize)
+ throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
+ const arr = new Uint8Array(2);
+ new DataView(arr.buffer).setUint16(0, num, false);
+ return arr;
+ }
+ function pad(plaintext) {
+ const unpadded = utf8Encoder.encode(plaintext);
+ const unpaddedLen = unpadded.length;
+ const prefix = writeU16BE(unpaddedLen);
+ const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);
+ return concatBytes3(prefix, unpadded, suffix);
+ }
+ function unpad(padded) {
+ const unpaddedLen = new DataView(padded.buffer).getUint16(0);
+ const unpadded = padded.subarray(2, 2 + unpaddedLen);
+ if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen))
+ throw new Error("invalid padding");
+ return utf8Decoder.decode(unpadded);
+ }
+ function hmacAad(key, message, aad) {
+ if (aad.length !== 32)
+ throw new Error("AAD associated data must be 32 bytes");
+ const combined = concatBytes3(aad, message);
+ return hmac2(sha2562, key, combined);
+ }
+ function decodePayload(payload) {
+ if (typeof payload !== "string")
+ throw new Error("payload must be a valid string");
+ const plen = payload.length;
+ if (plen < 132 || plen > 87472)
+ throw new Error("invalid payload length: " + plen);
+ if (payload[0] === "#")
+ throw new Error("unknown encryption version");
+ let data;
+ try {
+ data = base64.decode(payload);
+ } catch (error) {
+ throw new Error("invalid base64: " + error.message);
+ }
+ const dlen = data.length;
+ if (dlen < 99 || dlen > 65603)
+ throw new Error("invalid data length: " + dlen);
+ const vers = data[0];
+ if (vers !== 2)
+ throw new Error("unknown encryption version " + vers);
+ return {
+ nonce: data.subarray(1, 33),
+ ciphertext: data.subarray(33, -32),
+ mac: data.subarray(-32)
+ };
+ }
+ function encrypt3(plaintext, conversationKey, nonce = randomBytes2(32)) {
+ const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
+ const padded = pad(plaintext);
+ const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
+ const mac = hmacAad(hmac_key, ciphertext, nonce);
+ return base64.encode(concatBytes3(new Uint8Array([2]), nonce, ciphertext, mac));
+ }
+ function decrypt3(payload, conversationKey) {
+ const { nonce, ciphertext, mac } = decodePayload(payload);
+ const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
+ const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
+ if (!equalBytes2(calculatedMac, mac))
+ throw new Error("invalid MAC");
+ const padded = chacha20(chacha_key, chacha_nonce, ciphertext);
+ return unpad(padded);
+ }
+ var v2 = {
+ utils: {
+ getConversationKey,
+ calcPaddedLen
+ },
+ encrypt: encrypt3,
+ decrypt: decrypt3
+ };
+
+ // nip59.ts
+ var TWO_DAYS = 2 * 24 * 60 * 60;
+ var now = () => Math.round(Date.now() / 1e3);
+ var randomNow = () => Math.round(now() - Math.random() * TWO_DAYS);
+ var nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey);
+ var nip44Encrypt = (data, privateKey, publicKey) => encrypt3(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey));
+ var nip44Decrypt = (data, privateKey) => JSON.parse(decrypt3(data.content, nip44ConversationKey(privateKey, data.pubkey)));
+ function createRumor(event, privateKey) {
+ const rumor = {
+ created_at: now(),
+ content: "",
+ tags: [],
+ ...event,
+ pubkey: getPublicKey(privateKey)
+ };
+ rumor.id = getEventHash(rumor);
+ return rumor;
+ }
+ function createSeal(rumor, privateKey, recipientPublicKey) {
+ return finalizeEvent(
+ {
+ kind: Seal,
+ content: nip44Encrypt(rumor, privateKey, recipientPublicKey),
+ created_at: randomNow(),
+ tags: []
+ },
+ privateKey
+ );
+ }
+ function createWrap(seal, recipientPublicKey) {
+ const randomKey = generateSecretKey();
+ return finalizeEvent(
+ {
+ kind: GiftWrap,
+ content: nip44Encrypt(seal, randomKey, recipientPublicKey),
+ created_at: randomNow(),
+ tags: [["p", recipientPublicKey]]
+ },
+ randomKey
+ );
+ }
+ function wrapEvent(event, senderPrivateKey, recipientPublicKey) {
+ const rumor = createRumor(event, senderPrivateKey);
+ const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey);
+ return createWrap(seal, recipientPublicKey);
+ }
+ function wrapManyEvents(event, senderPrivateKey, recipientsPublicKeys) {
+ if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {
+ throw new Error("At least one recipient is required.");
+ }
+ const senderPublicKey = getPublicKey(senderPrivateKey);
+ const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)];
+ recipientsPublicKeys.forEach((recipientPublicKey) => {
+ wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey));
+ });
+ return wrappeds;
+ }
+ function unwrapEvent(wrap, recipientPrivateKey) {
+ const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey);
+ return nip44Decrypt(unwrappedSeal, recipientPrivateKey);
+ }
+ function unwrapManyEvents(wrappedEvents, recipientPrivateKey) {
+ let unwrappedEvents = [];
+ wrappedEvents.forEach((e) => {
+ unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey));
+ });
+ unwrappedEvents.sort((a, b) => a.created_at - b.created_at);
+ return unwrappedEvents;
+ }
+
+ // nip17.ts
+ function createEvent(recipients, message, conversationTitle, replyTo) {
+ const baseEvent = {
+ created_at: Math.ceil(Date.now() / 1e3),
+ kind: PrivateDirectMessage,
+ tags: [],
+ content: message
+ };
+ const recipientsArray = Array.isArray(recipients) ? recipients : [recipients];
+ recipientsArray.forEach(({ publicKey, relayUrl }) => {
+ baseEvent.tags.push(relayUrl ? ["p", publicKey, relayUrl] : ["p", publicKey]);
+ });
+ if (replyTo) {
+ baseEvent.tags.push(["e", replyTo.eventId, replyTo.relayUrl || "", "reply"]);
+ }
+ if (conversationTitle) {
+ baseEvent.tags.push(["subject", conversationTitle]);
+ }
+ return baseEvent;
+ }
+ function wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo) {
+ const event = createEvent(recipient, message, conversationTitle, replyTo);
+ return wrapEvent(event, senderPrivateKey, recipient.publicKey);
+ }
+ function wrapManyEvents2(senderPrivateKey, recipients, message, conversationTitle, replyTo) {
+ if (!recipients || recipients.length === 0) {
+ throw new Error("At least one recipient is required.");
+ }
+ const senderPublicKey = getPublicKey(senderPrivateKey);
+ return [{ publicKey: senderPublicKey }, ...recipients].map(
+ (recipient) => wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo)
+ );
+ }
+ var unwrapEvent2 = unwrapEvent;
+ var unwrapManyEvents2 = unwrapManyEvents;
+
+ // nip18.ts
+ var nip18_exports = {};
+ __export(nip18_exports, {
+ finishRepostEvent: () => finishRepostEvent,
+ getRepostedEvent: () => getRepostedEvent,
+ getRepostedEventPointer: () => getRepostedEventPointer
+ });
+ function finishRepostEvent(t, reposted, relayUrl, privateKey) {
+ let kind;
+ const tags = [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]];
+ if (reposted.kind === ShortTextNote) {
+ kind = Repost;
+ } else {
+ kind = GenericRepost;
+ tags.push(["k", String(reposted.kind)]);
+ }
+ return finalizeEvent(
+ {
+ kind,
+ tags,
+ content: t.content === "" || reposted.tags?.find((tag) => tag[0] === "-") ? "" : JSON.stringify(reposted),
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ }
+ function getRepostedEventPointer(event) {
+ if (![Repost, GenericRepost].includes(event.kind)) {
+ return void 0;
+ }
+ let lastETag;
+ let lastPTag;
+ for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) {
+ const tag = event.tags[i2];
+ if (tag.length >= 2) {
+ if (tag[0] === "e" && lastETag === void 0) {
+ lastETag = tag;
+ } else if (tag[0] === "p" && lastPTag === void 0) {
+ lastPTag = tag;
+ }
+ }
+ }
+ if (lastETag === void 0) {
+ return void 0;
+ }
+ return {
+ id: lastETag[1],
+ relays: [lastETag[2], lastPTag?.[2]].filter((x) => typeof x === "string"),
+ author: lastPTag?.[1]
+ };
+ }
+ function getRepostedEvent(event, { skipVerification } = {}) {
+ const pointer = getRepostedEventPointer(event);
+ if (pointer === void 0 || event.content === "") {
+ return void 0;
+ }
+ let repostedEvent;
+ try {
+ repostedEvent = JSON.parse(event.content);
+ } catch (error) {
+ return void 0;
+ }
+ if (repostedEvent.id !== pointer.id) {
+ return void 0;
+ }
+ if (!skipVerification && !verifyEvent(repostedEvent)) {
+ return void 0;
+ }
+ return repostedEvent;
+ }
+
+ // nip21.ts
+ var nip21_exports = {};
+ __export(nip21_exports, {
+ NOSTR_URI_REGEX: () => NOSTR_URI_REGEX,
+ parse: () => parse2,
+ test: () => test
+ });
+ var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`);
+ function test(value) {
+ return typeof value === "string" && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value);
+ }
+ function parse2(uri) {
+ const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`));
+ if (!match)
+ throw new Error(`Invalid Nostr URI: ${uri}`);
+ return {
+ uri: match[0],
+ value: match[1],
+ decoded: decode(match[1])
+ };
+ }
+
+ // nip25.ts
+ var nip25_exports = {};
+ __export(nip25_exports, {
+ finishReactionEvent: () => finishReactionEvent,
+ getReactedEventPointer: () => getReactedEventPointer
+ });
+ function finishReactionEvent(t, reacted, privateKey) {
+ const inheritedTags = reacted.tags.filter((tag) => tag.length >= 2 && (tag[0] === "e" || tag[0] === "p"));
+ return finalizeEvent(
+ {
+ ...t,
+ kind: Reaction,
+ tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]],
+ content: t.content ?? "+"
+ },
+ privateKey
+ );
+ }
+ function getReactedEventPointer(event) {
+ if (event.kind !== Reaction) {
+ return void 0;
+ }
+ let lastETag;
+ let lastPTag;
+ for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) {
+ const tag = event.tags[i2];
+ if (tag.length >= 2) {
+ if (tag[0] === "e" && lastETag === void 0) {
+ lastETag = tag;
+ } else if (tag[0] === "p" && lastPTag === void 0) {
+ lastPTag = tag;
+ }
+ }
+ }
+ if (lastETag === void 0 || lastPTag === void 0) {
+ return void 0;
+ }
+ return {
+ id: lastETag[1],
+ relays: [lastETag[2], lastPTag[2]].filter((x) => x !== void 0),
+ author: lastPTag[1]
+ };
+ }
+
+ // nip27.ts
+ var nip27_exports = {};
+ __export(nip27_exports, {
+ parse: () => parse3
+ });
+ var noCharacter = /\W/m;
+ var noURLCharacter = /\W |\W$|$|,| /m;
+ function* parse3(content) {
+ const max = content.length;
+ let prevIndex = 0;
+ let index = 0;
+ while (index < max) {
+ let u = content.indexOf(":", index);
+ if (u === -1) {
+ break;
+ }
+ if (content.substring(u - 5, u) === "nostr") {
+ const m = content.substring(u + 60).match(noCharacter);
+ const end = m ? u + 60 + m.index : max;
+ try {
+ let pointer;
+ let { data, type } = decode(content.substring(u + 1, end));
+ switch (type) {
+ case "npub":
+ pointer = { pubkey: data };
+ break;
+ case "nsec":
+ case "note":
+ index = end + 1;
+ continue;
+ default:
+ pointer = data;
+ }
+ if (prevIndex !== u - 5) {
+ yield { type: "text", text: content.substring(prevIndex, u - 5) };
+ }
+ yield { type: "reference", pointer };
+ index = end;
+ prevIndex = index;
+ continue;
+ } catch (_err) {
+ index = u + 1;
+ continue;
+ }
+ } else if (content.substring(u - 5, u) === "https" || content.substring(u - 4, u) === "http") {
+ const m = content.substring(u + 4).match(noURLCharacter);
+ const end = m ? u + 4 + m.index : max;
+ const prefixLen = content[u - 1] === "s" ? 5 : 4;
+ try {
+ let url = new URL(content.substring(u - prefixLen, end));
+ if (url.hostname.indexOf(".") === -1) {
+ throw new Error("invalid url");
+ }
+ if (prevIndex !== u - prefixLen) {
+ yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
+ }
+ if (/\.(png|jpe?g|gif|webp)$/i.test(url.pathname)) {
+ yield { type: "image", url: url.toString() };
+ index = end;
+ prevIndex = index;
+ continue;
+ }
+ if (/\.(mp4|avi|webm|mkv)$/i.test(url.pathname)) {
+ yield { type: "video", url: url.toString() };
+ index = end;
+ prevIndex = index;
+ continue;
+ }
+ if (/\.(mp3|aac|ogg|opus)$/i.test(url.pathname)) {
+ yield { type: "audio", url: url.toString() };
+ index = end;
+ prevIndex = index;
+ continue;
+ }
+ yield { type: "url", url: url.toString() };
+ index = end;
+ prevIndex = index;
+ continue;
+ } catch (_err) {
+ index = end + 1;
+ continue;
+ }
+ } else if (content.substring(u - 3, u) === "wss" || content.substring(u - 2, u) === "ws") {
+ const m = content.substring(u + 4).match(noURLCharacter);
+ const end = m ? u + 4 + m.index : max;
+ const prefixLen = content[u - 1] === "s" ? 3 : 2;
+ try {
+ let url = new URL(content.substring(u - prefixLen, end));
+ if (url.hostname.indexOf(".") === -1) {
+ throw new Error("invalid ws url");
+ }
+ if (prevIndex !== u - prefixLen) {
+ yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
+ }
+ yield { type: "relay", url: url.toString() };
+ index = end;
+ prevIndex = index;
+ continue;
+ } catch (_err) {
+ index = end + 1;
+ continue;
+ }
+ } else {
+ index = u + 1;
+ continue;
+ }
+ }
+ if (prevIndex !== max) {
+ yield { type: "text", text: content.substring(prevIndex) };
+ }
+ }
+
+ // nip28.ts
+ var nip28_exports = {};
+ __export(nip28_exports, {
+ channelCreateEvent: () => channelCreateEvent,
+ channelHideMessageEvent: () => channelHideMessageEvent,
+ channelMessageEvent: () => channelMessageEvent,
+ channelMetadataEvent: () => channelMetadataEvent,
+ channelMuteUserEvent: () => channelMuteUserEvent
+ });
+ var channelCreateEvent = (t, privateKey) => {
+ let content;
+ if (typeof t.content === "object") {
+ content = JSON.stringify(t.content);
+ } else if (typeof t.content === "string") {
+ content = t.content;
+ } else {
+ return void 0;
+ }
+ return finalizeEvent(
+ {
+ kind: ChannelCreation,
+ tags: [...t.tags ?? []],
+ content,
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ };
+ var channelMetadataEvent = (t, privateKey) => {
+ let content;
+ if (typeof t.content === "object") {
+ content = JSON.stringify(t.content);
+ } else if (typeof t.content === "string") {
+ content = t.content;
+ } else {
+ return void 0;
+ }
+ return finalizeEvent(
+ {
+ kind: ChannelMetadata,
+ tags: [["e", t.channel_create_event_id], ...t.tags ?? []],
+ content,
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ };
+ var channelMessageEvent = (t, privateKey) => {
+ const tags = [["e", t.channel_create_event_id, t.relay_url, "root"]];
+ if (t.reply_to_channel_message_event_id) {
+ tags.push(["e", t.reply_to_channel_message_event_id, t.relay_url, "reply"]);
+ }
+ return finalizeEvent(
+ {
+ kind: ChannelMessage,
+ tags: [...tags, ...t.tags ?? []],
+ content: t.content,
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ };
+ var channelHideMessageEvent = (t, privateKey) => {
+ let content;
+ if (typeof t.content === "object") {
+ content = JSON.stringify(t.content);
+ } else if (typeof t.content === "string") {
+ content = t.content;
+ } else {
+ return void 0;
+ }
+ return finalizeEvent(
+ {
+ kind: ChannelHideMessage,
+ tags: [["e", t.channel_message_event_id], ...t.tags ?? []],
+ content,
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ };
+ var channelMuteUserEvent = (t, privateKey) => {
+ let content;
+ if (typeof t.content === "object") {
+ content = JSON.stringify(t.content);
+ } else if (typeof t.content === "string") {
+ content = t.content;
+ } else {
+ return void 0;
+ }
+ return finalizeEvent(
+ {
+ kind: ChannelMuteUser,
+ tags: [["p", t.pubkey_to_mute], ...t.tags ?? []],
+ content,
+ created_at: t.created_at
+ },
+ privateKey
+ );
+ };
+
+ // nip30.ts
+ var nip30_exports = {};
+ __export(nip30_exports, {
+ EMOJI_SHORTCODE_REGEX: () => EMOJI_SHORTCODE_REGEX,
+ matchAll: () => matchAll,
+ regex: () => regex,
+ replaceAll: () => replaceAll
+ });
+ var EMOJI_SHORTCODE_REGEX = /:(\w+):/;
+ var regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, "g");
+ function* matchAll(content) {
+ const matches = content.matchAll(regex());
+ for (const match of matches) {
+ try {
+ const [shortcode, name] = match;
+ yield {
+ shortcode,
+ name,
+ start: match.index,
+ end: match.index + shortcode.length
+ };
+ } catch (_e) {
+ }
+ }
+ }
+ function replaceAll(content, replacer) {
+ return content.replaceAll(regex(), (shortcode, name) => {
+ return replacer({
+ shortcode,
+ name
+ });
+ });
+ }
+
+ // nip39.ts
+ var nip39_exports = {};
+ __export(nip39_exports, {
+ useFetchImplementation: () => useFetchImplementation3,
+ validateGithub: () => validateGithub
+ });
+ var _fetch3;
+ try {
+ _fetch3 = fetch;
+ } catch {
+ }
+ function useFetchImplementation3(fetchImplementation) {
+ _fetch3 = fetchImplementation;
+ }
+ async function validateGithub(pubkey, username, proof) {
+ try {
+ let res = await (await _fetch3(`https://gist.github.com/${username}/${proof}/raw`)).text();
+ return res === `Verifying that I control the following Nostr public key: ${pubkey}`;
+ } catch (_) {
+ return false;
+ }
+ }
+
+ // nip46.ts
+ var nip46_exports = {};
+ __export(nip46_exports, {
+ BUNKER_REGEX: () => BUNKER_REGEX,
+ BunkerSigner: () => BunkerSigner,
+ createAccount: () => createAccount,
+ createNostrConnectURI: () => createNostrConnectURI,
+ fetchBunkerProviders: () => fetchBunkerProviders,
+ parseBunkerInput: () => parseBunkerInput,
+ parseNostrConnectURI: () => parseNostrConnectURI,
+ queryBunkerProfile: () => queryBunkerProfile,
+ toBunkerURL: () => toBunkerURL,
+ useFetchImplementation: () => useFetchImplementation4
+ });
+ var _fetch4;
+ try {
+ _fetch4 = fetch;
+ } catch {
+ }
+ function useFetchImplementation4(fetchImplementation) {
+ _fetch4 = fetchImplementation;
+ }
+ var BUNKER_REGEX = /^bunker:\/\/([0-9a-f]{64})\??([?\/\w:.=&%-]*)$/;
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ function toBunkerURL(bunkerPointer) {
+ let bunkerURL = new URL(`bunker://${bunkerPointer.pubkey}`);
+ bunkerPointer.relays.forEach((relay) => {
+ bunkerURL.searchParams.append("relay", relay);
+ });
+ if (bunkerPointer.secret) {
+ bunkerURL.searchParams.set("secret", bunkerPointer.secret);
+ }
+ return bunkerURL.toString();
+ }
+ async function parseBunkerInput(input) {
+ let match = input.match(BUNKER_REGEX);
+ if (match) {
+ try {
+ const pubkey = match[1];
+ const qs = new URLSearchParams(match[2]);
+ return {
+ pubkey,
+ relays: qs.getAll("relay"),
+ secret: qs.get("secret")
+ };
+ } catch (_err) {
+ }
+ }
+ return queryBunkerProfile(input);
+ }
+ async function queryBunkerProfile(nip05) {
+ const match = nip05.match(NIP05_REGEX);
+ if (!match)
+ return null;
+ const [_, name = "_", domain] = match;
+ try {
+ const url = `https://${domain}/.well-known/nostr.json?name=${name}`;
+ const res = await (await _fetch4(url, { redirect: "error" })).json();
+ let pubkey = res.names[name];
+ let relays = res.nip46[pubkey] || [];
+ return { pubkey, relays, secret: null };
+ } catch (_err) {
+ return null;
+ }
+ }
+ function createNostrConnectURI(params) {
+ if (!params.clientPubkey) {
+ throw new Error("clientPubkey is required.");
+ }
+ if (!params.relays || params.relays.length === 0) {
+ throw new Error("At least one relay is required.");
+ }
+ if (!params.secret) {
+ throw new Error("secret is required.");
+ }
+ const queryParams = new URLSearchParams();
+ params.relays.forEach((relay) => {
+ queryParams.append("relay", relay);
+ });
+ queryParams.append("secret", params.secret);
+ if (params.perms && params.perms.length > 0) {
+ queryParams.append("perms", params.perms.join(","));
+ }
+ if (params.name) {
+ queryParams.append("name", params.name);
+ }
+ if (params.url) {
+ queryParams.append("url", params.url);
+ }
+ if (params.image) {
+ queryParams.append("image", params.image);
+ }
+ return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`;
+ }
+ function parseNostrConnectURI(uri) {
+ if (!uri.startsWith("nostrconnect://")) {
+ throw new Error('Invalid nostrconnect URI: Must start with "nostrconnect://".');
+ }
+ const [protocolAndPubkey, queryString] = uri.split("?");
+ if (!protocolAndPubkey || !queryString) {
+ throw new Error("Invalid nostrconnect URI: Missing query string.");
+ }
+ const clientPubkey = protocolAndPubkey.substring("nostrconnect://".length);
+ if (!clientPubkey) {
+ throw new Error("Invalid nostrconnect URI: Missing client-pubkey.");
+ }
+ const queryParams = new URLSearchParams(queryString);
+ const relays = queryParams.getAll("relay");
+ if (relays.length === 0) {
+ throw new Error('Invalid nostrconnect URI: Missing "relay" parameter.');
+ }
+ const secret = queryParams.get("secret");
+ if (!secret) {
+ throw new Error('Invalid nostrconnect URI: Missing "secret" parameter.');
+ }
+ const permsString = queryParams.get("perms");
+ const perms = permsString ? permsString.split(",") : void 0;
+ const name = queryParams.get("name") || void 0;
+ const url = queryParams.get("url") || void 0;
+ const image = queryParams.get("image") || void 0;
+ return {
+ protocol: "nostrconnect",
+ clientPubkey,
+ params: {
+ relays,
+ secret,
+ perms,
+ name,
+ url,
+ image
+ },
+ originalString: uri
+ };
+ }
+ var BunkerSigner = class {
+ params;
+ pool;
+ subCloser;
+ isOpen;
+ serial;
+ idPrefix;
+ listeners;
+ waitingForAuth;
+ secretKey;
+ conversationKey;
+ bp;
+ cachedPubKey;
+ constructor(clientSecretKey, params) {
+ this.params = params;
+ this.pool = params.pool || new SimplePool();
+ this.secretKey = clientSecretKey;
+ this.isOpen = false;
+ this.idPrefix = Math.random().toString(36).substring(7);
+ this.serial = 0;
+ this.listeners = {};
+ this.waitingForAuth = {};
+ }
+ static fromBunker(clientSecretKey, bp, params = {}) {
+ if (bp.relays.length === 0) {
+ throw new Error("No relays specified for this bunker");
+ }
+ const signer = new BunkerSigner(clientSecretKey, params);
+ signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey);
+ signer.bp = bp;
+ signer.setupSubscription(params);
+ return signer;
+ }
+ static async fromURI(clientSecretKey, connectionURI, params = {}, maxWait = 3e5) {
+ const signer = new BunkerSigner(clientSecretKey, params);
+ const parsedURI = parseNostrConnectURI(connectionURI);
+ const clientPubkey = getPublicKey(clientSecretKey);
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => {
+ sub.close();
+ reject(new Error(`Connection timed out after ${maxWait / 1e3} seconds`));
+ }, maxWait);
+ const sub = signer.pool.subscribe(
+ parsedURI.params.relays,
+ { kinds: [NostrConnect], "#p": [clientPubkey] },
+ {
+ onevent: async (event) => {
+ try {
+ const tempConvKey = getConversationKey(clientSecretKey, event.pubkey);
+ const decryptedContent = decrypt3(event.content, tempConvKey);
+ const response = JSON.parse(decryptedContent);
+ if (response.result === parsedURI.params.secret) {
+ clearTimeout(timer);
+ sub.close();
+ signer.bp = {
+ pubkey: event.pubkey,
+ relays: parsedURI.params.relays,
+ secret: parsedURI.params.secret
+ };
+ signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey);
+ signer.setupSubscription(params);
+ resolve(signer);
+ }
+ } catch (e) {
+ console.warn("Failed to process potential connection event", e);
+ }
+ },
+ onclose: () => {
+ clearTimeout(timer);
+ reject(new Error("Subscription closed before connection was established."));
+ },
+ maxWait
+ }
+ );
+ });
+ }
+ setupSubscription(params) {
+ const listeners = this.listeners;
+ const waitingForAuth = this.waitingForAuth;
+ const convKey = this.conversationKey;
+ this.subCloser = this.pool.subscribe(
+ this.bp.relays,
+ { kinds: [NostrConnect], authors: [this.bp.pubkey], "#p": [getPublicKey(this.secretKey)] },
+ {
+ onevent: async (event) => {
+ const o = JSON.parse(decrypt3(event.content, convKey));
+ const { id, result, error } = o;
+ if (result === "auth_url" && waitingForAuth[id]) {
+ delete waitingForAuth[id];
+ if (params.onauth) {
+ params.onauth(error);
+ } else {
+ console.warn(
+ `nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an "auth_url"='${error}' but there was no onauth() callback configured.`
+ );
+ }
+ return;
+ }
+ let handler = listeners[id];
+ if (handler) {
+ if (error)
+ handler.reject(error);
+ else if (result)
+ handler.resolve(result);
+ delete listeners[id];
+ }
+ },
+ onclose: () => {
+ this.subCloser = void 0;
+ }
+ }
+ );
+ this.isOpen = true;
+ }
+ async close() {
+ this.isOpen = false;
+ this.subCloser.close();
+ }
+ async sendRequest(method, params) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ if (!this.isOpen)
+ throw new Error("this signer is not open anymore, create a new one");
+ if (!this.subCloser)
+ this.setupSubscription(this.params);
+ this.serial++;
+ const id = `${this.idPrefix}-${this.serial}`;
+ const encryptedContent = encrypt3(JSON.stringify({ id, method, params }), this.conversationKey);
+ const verifiedEvent = finalizeEvent(
+ {
+ kind: NostrConnect,
+ tags: [["p", this.bp.pubkey]],
+ content: encryptedContent,
+ created_at: Math.floor(Date.now() / 1e3)
+ },
+ this.secretKey
+ );
+ this.listeners[id] = { resolve, reject };
+ this.waitingForAuth[id] = true;
+ await Promise.any(this.pool.publish(this.bp.relays, verifiedEvent));
+ } catch (err) {
+ reject(err);
+ }
+ });
+ }
+ async ping() {
+ let resp = await this.sendRequest("ping", []);
+ if (resp !== "pong")
+ throw new Error(`result is not pong: ${resp}`);
+ }
+ async connect() {
+ await this.sendRequest("connect", [this.bp.pubkey, this.bp.secret || ""]);
+ }
+ async getPublicKey() {
+ if (!this.cachedPubKey) {
+ this.cachedPubKey = await this.sendRequest("get_public_key", []);
+ }
+ return this.cachedPubKey;
+ }
+ async signEvent(event) {
+ let resp = await this.sendRequest("sign_event", [JSON.stringify(event)]);
+ let signed = JSON.parse(resp);
+ if (verifyEvent(signed)) {
+ return signed;
+ } else {
+ throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(signed)}`);
+ }
+ }
+ async nip04Encrypt(thirdPartyPubkey, plaintext) {
+ return await this.sendRequest("nip04_encrypt", [thirdPartyPubkey, plaintext]);
+ }
+ async nip04Decrypt(thirdPartyPubkey, ciphertext) {
+ return await this.sendRequest("nip04_decrypt", [thirdPartyPubkey, ciphertext]);
+ }
+ async nip44Encrypt(thirdPartyPubkey, plaintext) {
+ return await this.sendRequest("nip44_encrypt", [thirdPartyPubkey, plaintext]);
+ }
+ async nip44Decrypt(thirdPartyPubkey, ciphertext) {
+ return await this.sendRequest("nip44_decrypt", [thirdPartyPubkey, ciphertext]);
+ }
+ };
+ async function createAccount(bunker, params, username, domain, email, localSecretKey = generateSecretKey()) {
+ if (email && !EMAIL_REGEX.test(email))
+ throw new Error("Invalid email");
+ let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params);
+ let pubkey = await rpc.sendRequest("create_account", [username, domain, email || ""]);
+ rpc.bp.pubkey = pubkey;
+ await rpc.connect();
+ return rpc;
+ }
+ async function fetchBunkerProviders(pool, relays) {
+ const events = await pool.querySync(relays, {
+ kinds: [Handlerinformation],
+ "#k": [NostrConnect.toString()]
+ });
+ events.sort((a, b) => b.created_at - a.created_at);
+ const validatedBunkers = await Promise.all(
+ events.map(async (event, i2) => {
+ try {
+ const content = JSON.parse(event.content);
+ try {
+ if (events.findIndex((ev) => JSON.parse(ev.content).nip05 === content.nip05) !== i2)
+ return void 0;
+ } catch (err) {
+ }
+ const bp = await queryBunkerProfile(content.nip05);
+ if (bp && bp.pubkey === event.pubkey && bp.relays.length) {
+ return {
+ bunkerPointer: bp,
+ nip05: content.nip05,
+ domain: content.nip05.split("@")[1],
+ name: content.name || content.display_name,
+ picture: content.picture,
+ about: content.about,
+ website: content.website,
+ local: false
+ };
+ }
+ } catch (err) {
+ return void 0;
+ }
+ })
+ );
+ return validatedBunkers.filter((b) => b !== void 0);
+ }
+
+ // nip47.ts
+ var nip47_exports = {};
+ __export(nip47_exports, {
+ makeNwcRequestEvent: () => makeNwcRequestEvent,
+ parseConnectionString: () => parseConnectionString
+ });
+ function parseConnectionString(connectionString) {
+ const { host, pathname, searchParams } = new URL(connectionString);
+ const pubkey = pathname || host;
+ const relay = searchParams.get("relay");
+ const secret = searchParams.get("secret");
+ if (!pubkey || !relay || !secret) {
+ throw new Error("invalid connection string");
+ }
+ return { pubkey, relay, secret };
+ }
+ async function makeNwcRequestEvent(pubkey, secretKey, invoice) {
+ const content = {
+ method: "pay_invoice",
+ params: {
+ invoice
+ }
+ };
+ const encryptedContent = encrypt2(secretKey, pubkey, JSON.stringify(content));
+ const eventTemplate = {
+ kind: NWCWalletRequest,
+ created_at: Math.round(Date.now() / 1e3),
+ content: encryptedContent,
+ tags: [["p", pubkey]]
+ };
+ return finalizeEvent(eventTemplate, secretKey);
+ }
+
+ // nip54.ts
+ var nip54_exports = {};
+ __export(nip54_exports, {
+ normalizeIdentifier: () => normalizeIdentifier
+ });
+ function normalizeIdentifier(name) {
+ name = name.trim().toLowerCase();
+ name = name.normalize("NFKC");
+ return Array.from(name).map((char) => {
+ if (/\p{Letter}/u.test(char) || /\p{Number}/u.test(char)) {
+ return char;
+ }
+ return "-";
+ }).join("");
+ }
+
+ // nip57.ts
+ var nip57_exports = {};
+ __export(nip57_exports, {
+ getSatoshisAmountFromBolt11: () => getSatoshisAmountFromBolt11,
+ getZapEndpoint: () => getZapEndpoint,
+ makeZapReceipt: () => makeZapReceipt,
+ makeZapRequest: () => makeZapRequest,
+ useFetchImplementation: () => useFetchImplementation5,
+ validateZapRequest: () => validateZapRequest
+ });
+ var _fetch5;
+ try {
+ _fetch5 = fetch;
+ } catch {
+ }
+ function useFetchImplementation5(fetchImplementation) {
+ _fetch5 = fetchImplementation;
+ }
+ async function getZapEndpoint(metadata) {
+ try {
+ let lnurl = "";
+ let { lud06, lud16 } = JSON.parse(metadata.content);
+ if (lud06) {
+ let { words } = bech32.decode(lud06, 1e3);
+ let data = bech32.fromWords(words);
+ lnurl = utf8Decoder.decode(data);
+ } else if (lud16) {
+ let [name, domain] = lud16.split("@");
+ lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString();
+ } else {
+ return null;
+ }
+ let res = await _fetch5(lnurl);
+ let body = await res.json();
+ if (body.allowsNostr && body.nostrPubkey) {
+ return body.callback;
+ }
+ } catch (err) {
+ }
+ return null;
+ }
+ function makeZapRequest(params) {
+ let zr = {
+ kind: 9734,
+ created_at: Math.round(Date.now() / 1e3),
+ content: params.comment || "",
+ tags: [
+ ["p", "pubkey" in params ? params.pubkey : params.event.pubkey],
+ ["amount", params.amount.toString()],
+ ["relays", ...params.relays]
+ ]
+ };
+ if ("event" in params) {
+ zr.tags.push(["e", params.event.id]);
+ if (isReplaceableKind(params.event.kind)) {
+ const a = ["a", `${params.event.kind}:${params.event.pubkey}:`];
+ zr.tags.push(a);
+ } else if (isAddressableKind(params.event.kind)) {
+ let d = params.event.tags.find(([t, v]) => t === "d" && v);
+ if (!d)
+ throw new Error("d tag not found or is empty");
+ const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d[1]}`];
+ zr.tags.push(a);
+ }
+ zr.tags.push(["k", params.event.kind.toString()]);
+ }
+ return zr;
+ }
+ function validateZapRequest(zapRequestString) {
+ let zapRequest;
+ try {
+ zapRequest = JSON.parse(zapRequestString);
+ } catch (err) {
+ return "Invalid zap request JSON.";
+ }
+ if (!validateEvent(zapRequest))
+ return "Zap request is not a valid Nostr event.";
+ if (!verifyEvent(zapRequest))
+ return "Invalid signature on zap request.";
+ let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
+ if (!p)
+ return "Zap request doesn't have a 'p' tag.";
+ if (!p[1].match(/^[a-f0-9]{64}$/))
+ return "Zap request 'p' tag is not valid hex.";
+ let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
+ if (e && !e[1].match(/^[a-f0-9]{64}$/))
+ return "Zap request 'e' tag is not valid hex.";
+ let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
+ if (!relays)
+ return "Zap request doesn't have a 'relays' tag.";
+ return null;
+ }
+ function makeZapReceipt({
+ zapRequest,
+ preimage,
+ bolt11,
+ paidAt
+ }) {
+ let zr = JSON.parse(zapRequest);
+ let tagsFromZapRequest = zr.tags.filter(([t]) => t === "e" || t === "p" || t === "a");
+ let zap = {
+ kind: 9735,
+ created_at: Math.round(paidAt.getTime() / 1e3),
+ content: "",
+ tags: [...tagsFromZapRequest, ["P", zr.pubkey], ["bolt11", bolt11], ["description", zapRequest]]
+ };
+ if (preimage) {
+ zap.tags.push(["preimage", preimage]);
+ }
+ return zap;
+ }
+ function getSatoshisAmountFromBolt11(bolt11) {
+ if (bolt11.length < 50) {
+ return 0;
+ }
+ bolt11 = bolt11.substring(0, 50);
+ const idx = bolt11.lastIndexOf("1");
+ if (idx === -1) {
+ return 0;
+ }
+ const hrp = bolt11.substring(0, idx);
+ if (!hrp.startsWith("lnbc")) {
+ return 0;
+ }
+ const amount = hrp.substring(4);
+ if (amount.length < 1) {
+ return 0;
+ }
+ const char = amount[amount.length - 1];
+ const digit = char.charCodeAt(0) - "0".charCodeAt(0);
+ const isDigit = digit >= 0 && digit <= 9;
+ let cutPoint = amount.length - 1;
+ if (isDigit) {
+ cutPoint++;
+ }
+ if (cutPoint < 1) {
+ return 0;
+ }
+ const num = parseInt(amount.substring(0, cutPoint));
+ switch (char) {
+ case "m":
+ return num * 1e5;
+ case "u":
+ return num * 100;
+ case "n":
+ return num / 10;
+ case "p":
+ return num / 1e4;
+ default:
+ return num * 1e8;
+ }
+ }
+
+ // nip98.ts
+ var nip98_exports = {};
+ __export(nip98_exports, {
+ getToken: () => getToken,
+ hashPayload: () => hashPayload,
+ unpackEventFromToken: () => unpackEventFromToken,
+ validateEvent: () => validateEvent2,
+ validateEventKind: () => validateEventKind,
+ validateEventMethodTag: () => validateEventMethodTag,
+ validateEventPayloadTag: () => validateEventPayloadTag,
+ validateEventTimestamp: () => validateEventTimestamp,
+ validateEventUrlTag: () => validateEventUrlTag,
+ validateToken: () => validateToken
+ });
+ var _authorizationScheme = "Nostr ";
+ async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) {
+ const event = {
+ kind: HTTPAuth,
+ tags: [
+ ["u", loginUrl],
+ ["method", httpMethod]
+ ],
+ created_at: Math.round(new Date().getTime() / 1e3),
+ content: ""
+ };
+ if (payload) {
+ event.tags.push(["payload", hashPayload(payload)]);
+ }
+ const signedEvent = await sign(event);
+ const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : "";
+ return authorizationScheme + base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)));
+ }
+ async function validateToken(token, url, method) {
+ const event = await unpackEventFromToken(token).catch((error) => {
+ throw error;
+ });
+ const valid = await validateEvent2(event, url, method).catch((error) => {
+ throw error;
+ });
+ return valid;
+ }
+ async function unpackEventFromToken(token) {
+ if (!token) {
+ throw new Error("Missing token");
+ }
+ token = token.replace(_authorizationScheme, "");
+ const eventB64 = utf8Decoder.decode(base64.decode(token));
+ if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) {
+ throw new Error("Invalid token");
+ }
+ const event = JSON.parse(eventB64);
+ return event;
+ }
+ function validateEventTimestamp(event) {
+ if (!event.created_at) {
+ return false;
+ }
+ return Math.round(new Date().getTime() / 1e3) - event.created_at < 60;
+ }
+ function validateEventKind(event) {
+ return event.kind === HTTPAuth;
+ }
+ function validateEventUrlTag(event, url) {
+ const urlTag = event.tags.find((t) => t[0] === "u");
+ if (!urlTag) {
+ return false;
+ }
+ return urlTag.length > 0 && urlTag[1] === url;
+ }
+ function validateEventMethodTag(event, method) {
+ const methodTag = event.tags.find((t) => t[0] === "method");
+ if (!methodTag) {
+ return false;
+ }
+ return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase();
+ }
+ function hashPayload(payload) {
+ const hash3 = sha2562(utf8Encoder.encode(JSON.stringify(payload)));
+ return bytesToHex2(hash3);
+ }
+ function validateEventPayloadTag(event, payload) {
+ const payloadTag = event.tags.find((t) => t[0] === "payload");
+ if (!payloadTag) {
+ return false;
+ }
+ const payloadHash = hashPayload(payload);
+ return payloadTag.length > 0 && payloadTag[1] === payloadHash;
+ }
+ async function validateEvent2(event, url, method, body) {
+ if (!verifyEvent(event)) {
+ throw new Error("Invalid nostr event, signature invalid");
+ }
+ if (!validateEventKind(event)) {
+ throw new Error("Invalid nostr event, kind invalid");
+ }
+ if (!validateEventTimestamp(event)) {
+ throw new Error("Invalid nostr event, created_at timestamp invalid");
+ }
+ if (!validateEventUrlTag(event, url)) {
+ throw new Error("Invalid nostr event, url tag invalid");
+ }
+ if (!validateEventMethodTag(event, method)) {
+ throw new Error("Invalid nostr event, method tag invalid");
+ }
+ if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) {
+ if (!validateEventPayloadTag(event, body)) {
+ throw new Error("Invalid nostr event, payload tag does not match request body hash");
+ }
+ }
+ return true;
+ }
+ return __toCommonJS(nostr_tools_exports);
+})();
diff --git a/admin/assets/style.css b/admin/assets/style.css
deleted file mode 100644
index 37e5bda..0000000
--- a/admin/assets/style.css
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * C-Relay-PG Admin — PHP caching service admin pages.
- * Styled to match the original api/index.css Nostr admin interface.
- * Courier New monospace, black/white/red color scheme, dark mode by default.
- */
-
-:root {
- /* Core Variables — matches api/index.css light mode (default) */
- --primary-color: #000000;
- --secondary-color: #ffffff;
- --accent-color: #ff0000;
- --muted-color: #dddddd;
- --border-color: var(--muted-color);
- --font-family: "Courier New", Courier, monospace;
- --border-radius: 5px;
- --border-width: 1px;
-}
-
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-body {
- font-family: var(--font-family);
- background-color: var(--secondary-color);
- color: var(--primary-color);
- padding: 0;
- max-width: 1200px;
- margin: 0 auto;
-}
-
-a {
- color: var(--primary-color);
- text-decoration: none;
-}
-a:hover { color: var(--accent-color); }
-
-/* ================================
- SIDE NAVIGATION (matches original)
- ================================ */
-.side-nav {
- position: fixed;
- top: 0;
- left: -300px;
- width: 280px;
- height: 100vh;
- background: var(--secondary-color);
- border-right: var(--border-width) solid var(--border-color);
- z-index: 1000;
- transition: left 0.3s ease;
- overflow-y: auto;
- padding-top: 80px;
-}
-.side-nav.open { left: 0; }
-
-.side-nav-overlay {
- position: fixed;
- top: 0; left: 0; width: 100%; height: 100%;
- background: rgba(0, 0, 0, 0.5);
- z-index: 999;
- display: none;
-}
-.side-nav-overlay.show { display: block; }
-
-.nav-menu { list-style: none; padding: 0; margin: 0; }
-.nav-menu li { border-bottom: var(--border-width) solid var(--muted-color); }
-.nav-menu li:last-child { border-bottom: none; }
-
-.nav-item {
- display: block;
- padding: 15px 20px;
- color: var(--primary-color);
- text-decoration: none;
- font-family: var(--font-family);
- font-size: 16px;
- font-weight: bold;
- transition: all 0.2s ease;
- cursor: pointer;
- border: 2px solid var(--secondary-color);
- background: none;
- width: 100%;
- text-align: left;
-}
-.nav-item:hover {
- background: var(--muted-color);
- color: var(--accent-color);
-}
-.nav-item.active {
- text-decoration: underline;
- padding-left: 16px;
-}
-
-.nav-footer {
- position: absolute;
- bottom: 20px; left: 0; right: 0;
- padding: 0 20px;
-}
-.nav-footer-btn {
- display: block;
- width: 100%;
- padding: 12px 20px;
- margin-bottom: 8px;
- color: var(--primary-color);
- border: 1px solid var(--border-color);
- border-radius: 4px;
- font-family: var(--font-family);
- font-size: 14px;
- font-weight: bold;
- cursor: pointer;
- transition: all 0.2s ease;
- background: none;
-}
-.nav-footer-btn:hover {
- background: var(--muted-color);
- border-color: var(--accent-color);
-}
-
-/* ================================
- HEADER (matches original)
- ================================ */
-.main-header {
- background-color: var(--secondary-color);
- padding: 15px 20px;
- z-index: 100;
- max-width: 1200px;
- margin: 0 auto;
-}
-.header-content {
- display: flex;
- justify-content: space-between;
- align-items: center;
- position: relative;
-}
-.header-title {
- margin: 0;
- font-size: 24px;
- font-weight: bolder;
- color: var(--primary-color);
- cursor: pointer;
- transition: all 0.2s ease;
-}
-.header-title:hover { opacity: 0.8; }
-.header-title .relay-letter { display: inline-block; }
-
-.menu-btn {
- background: none;
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- color: var(--primary-color);
- font-family: var(--font-family);
- font-size: 20px;
- padding: 5px 12px;
- cursor: pointer;
- width: auto;
- margin: 0;
-}
-.menu-btn:hover { border-color: var(--accent-color); }
-
-/* ================================
- SECTIONS (matches original)
- ================================ */
-.section {
- background: var(--secondary-color);
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- padding: 20px;
- margin-bottom: 20px;
- margin-left: 5px;
- margin-right: 5px;
-}
-.section-header {
- display: flex;
- justify-content: center;
- align-items: center;
- padding-bottom: 15px;
- font-size: 16px;
- font-weight: normal;
- font-family: var(--font-family);
- color: var(--primary-color);
-}
-
-/* ================================
- TABLES (matches .config-table)
- ================================ */
-.config-table {
- border: 1px solid var(--border-color);
- border-radius: var(--border-radius);
- width: 100%;
- border-collapse: separate;
- border-spacing: 0;
- margin: 10px 0;
- overflow: hidden;
-}
-.config-table th,
-.config-table td {
- border: 0.1px solid var(--muted-color);
- padding: 4px 8px;
- text-align: left;
- font-family: var(--font-family);
- font-size: 10px;
-}
-.config-table th {
- font-weight: bold;
- height: 24px;
- line-height: 24px;
-}
-.config-table tbody tr:hover {
- background-color: rgba(0, 0, 0, 0.05);
-}
-.config-table-container {
- overflow-x: auto;
- max-width: 100%;
-}
-
-/* ================================
- INPUTS / BUTTONS (matches original)
- ================================ */
-input, textarea, select {
- width: 100%;
- padding: 8px;
- background: var(--secondary-color);
- color: var(--primary-color);
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- font-family: var(--font-family);
- font-size: 14px;
- box-sizing: border-box;
- transition: all 0.2s ease;
-}
-input:focus, textarea:focus, select:focus {
- border-color: var(--accent-color);
- outline: none;
-}
-
-button {
- width: 100%;
- padding: 8px;
- background: var(--secondary-color);
- color: var(--primary-color);
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- font-family: var(--font-family);
- font-size: 14px;
- cursor: pointer;
- margin: 5px 0;
- font-weight: bold;
- transition: all 0.2s ease;
-}
-button:hover { border-color: var(--accent-color); }
-button:active {
- background: var(--accent-color);
- color: var(--secondary-color);
-}
-
-/* ================================
- STAT CARDS (dashboard)
- ================================ */
-.cards {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
- gap: 10px;
- margin-bottom: 20px;
-}
-.card {
- background: var(--secondary-color);
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- padding: 12px;
-}
-.card .label {
- color: var(--muted-color);
- font-size: 10px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
-}
-.card .value {
- font-size: 20px;
- font-weight: bold;
- margin-top: 4px;
-}
-.card .sub {
- color: var(--muted-color);
- font-size: 10px;
- margin-top: 4px;
-}
-
-/* ================================
- STATUS BADGES
- ================================ */
-.badge {
- display: inline-block;
- padding: 2px 6px;
- border-radius: 3px;
- font-size: 10px;
- font-weight: bold;
- border: var(--border-width) solid var(--border-color);
-}
-.badge-success { border-color: #4caf50; color: #4caf50; }
-.badge-error { border-color: var(--accent-color); color: var(--accent-color); }
-.badge-warning { border-color: #ff9800; color: #ff9800; }
-.badge-muted { border-color: var(--muted-color); color: var(--muted-color); }
-
-/* ================================
- STATUS INDICATORS
- ================================ */
-.status-working { color: var(--accent-color); }
-.status-complete { color: #4caf50; }
-.status-error { color: var(--accent-color); }
-
-/* ================================
- PAGINATION
- ================================ */
-.pagination {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 12px 0;
- gap: 16px;
-}
-.pagination a {
- padding: 6px 14px;
- background: var(--secondary-color);
- border: var(--border-width) solid var(--border-color);
- border-radius: var(--border-radius);
- color: var(--primary-color);
- font-size: 14px;
- width: auto;
-}
-.pagination a:hover {
- border-color: var(--accent-color);
- color: var(--accent-color);
- text-decoration: none;
-}
-.pagination span { color: var(--muted-color); font-size: 12px; }
-
-/* ================================
- FILTERS BAR
- ================================ */
-.filters {
- display: flex;
- gap: 10px;
- margin-bottom: 16px;
- flex-wrap: wrap;
- align-items: center;
-}
-.filters select, .filters input {
- width: auto;
- min-width: 120px;
-}
-.filters input[type="text"] { min-width: 250px; }
-.filters button {
- width: auto;
- margin: 0;
- min-width: 80px;
-}
-
-/* ================================
- CONFIG EDITOR FORM
- ================================ */
-.config-form .row {
- display: grid;
- grid-template-columns: 250px 1fr;
- gap: 12px;
- padding: 12px 0;
- border-bottom: 0.1px solid var(--muted-color);
- align-items: start;
-}
-.config-form .row label {
- font-weight: bold;
- color: var(--primary-color);
- font-size: 12px;
-}
-.config-form .row .hint {
- color: var(--muted-color);
- font-size: 10px;
- margin-top: 4px;
-}
-.config-form button {
- width: auto;
- margin-top: 16px;
- padding: 10px 24px;
-}
-
-/* ================================
- MISC
- ================================ */
-.pubkey {
- font-family: var(--font-family);
- font-size: 10px;
- color: var(--muted-color);
-}
-.npub-link {
- font-family: var(--font-family);
- font-size: 10px;
- color: var(--primary-color);
- text-decoration: none;
-}
-.npub-link:hover { color: var(--accent-color); }
-
-h2 {
- font-weight: normal;
- text-align: center;
- font-size: 16px;
- font-family: var(--font-family);
- color: var(--primary-color);
- margin-bottom: 16px;
-}
-
-/* ================================
- RESPONSIVE
- ================================ */
-@media (max-width: 768px) {
- .cards { grid-template-columns: 1fr 1fr; }
- .config-form .row { grid-template-columns: 1fr; }
- .filters { flex-direction: column; align-items: stretch; }
- .filters select, .filters input, .filters button { width: 100%; }
-}
diff --git a/admin/cache/chart_day.txt b/admin/cache/chart_day.txt
new file mode 100644
index 0000000..4d41a59
--- /dev/null
+++ b/admin/cache/chart_day.txt
@@ -0,0 +1,15 @@
+ New Events — Last 24 Hours
+
+ 11 | X
+ 10 | XX
+ 9 | XX
+ 8 | XX
+ 7 | XX X X
+ 6 | XX X X
+ 5 | XX X X
+ 4 | X XX X XX
+ 3 | X XXX X XX
+ 2 | X X X XXX X XX
+ 1 | X XX X X X X X X X X X X X X XX X X XXXX X X X X X XX
+ +--------------------------------------------------------------------------------
+ 0s 1h 3h 4h 6h 7h 9h 10h 12h 13h 15h 16h 18h 19h 21h 22h
diff --git a/admin/cache/chart_month.txt b/admin/cache/chart_month.txt
new file mode 100644
index 0000000..2c53aab
--- /dev/null
+++ b/admin/cache/chart_month.txt
@@ -0,0 +1,15 @@
+ New Events — Last 30 Days
+
+ 81 | X
+ 73 | XX X
+ 65 | XX X
+ 57 | XX X X X
+ 49 | X X XX XX X X X
+ 41 | X X X XX X X X XXX XXX XXX X X X X
+ 33 | X X X XXXX XX X X X XX XXX XXXXXXXX XX X X X X X XX
+ 25 |X XX XX XX XXXX XX XXX X XX XXXXXXXXXXXXXXXXXXXX XX XXX X XX XX XXXXX
+ 17 |X XXXXX XX XXXXXXXXXXXXXXX X XX XXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXX XX XXXXXXXX
+ 9 |XXX XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX
+ 1 |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ +--------------------------------------------------------------------------------
+ 0s 1d 3d 5d 7d 9d 11d 13d 15d 16d 18d 20d 22d 24d 26d 28d
diff --git a/admin/cache/chart_year.txt b/admin/cache/chart_year.txt
new file mode 100644
index 0000000..1c3f4be
--- /dev/null
+++ b/admin/cache/chart_year.txt
@@ -0,0 +1,15 @@
+ New Events — Last Year
+
+531 | X XX
+478 | X X X XXX X
+425 | X X XX X XX XXX X
+372 | XXX XX X XX XXX X
+319 |XXXXXXXXXXX X X X XXXXXXX X
+266 |XXXXXXXXXXXX X XX XXX XXXXXXXXX X
+213 |XXXXXXXXXXXXXX XXX XXXXXXXXXXXXX XX X
+160 |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X X XXXX X
+107 |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX XX XX X X X XX XXX X XXXXXXX X
+ 54 |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXX
+ 1 |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ +--------------------------------------------------------------------------------
+ 0s 22d 45d 68d 91d 114d 136d 159d 182d 205d 228d 250d 273d 296d 319d 342d
diff --git a/admin/config-edit.php b/admin/config-edit.php
deleted file mode 100644
index 66acc7e..0000000
--- a/admin/config-edit.php
+++ /dev/null
@@ -1,99 +0,0 @@
-beginTransaction();
- foreach ($fields as $key) {
- $val = $_POST[$key] ?? null;
- if ($val === null) continue;
- $pdo->prepare("UPDATE config SET value = ? WHERE key = ?")->execute([$val, $key]);
- }
- $gen = $pdo->query("SELECT value FROM config WHERE key = 'caching_config_generation'")->fetchColumn();
- $new_gen = intval($gen) + 1;
- $pdo->prepare("UPDATE config SET value = ? WHERE key = 'caching_config_generation'")->execute([$new_gen]);
- $pdo->commit();
- $message = "Configuration saved. Config generation bumped to $new_gen. The caching service will reload automatically.";
- } catch (Exception $e) {
- $pdo->rollBack();
- $error = 'Failed to save: ' . $e->getMessage();
- }
-}
-
-$config_keys = [
- 'caching_root_npubs' => 'Root npubs (comma-separated npub... values)',
- 'caching_bootstrap_relays' => 'Bootstrap relays (comma-separated wss://... URLs)',
- 'caching_kinds' => 'Kinds to cache (comma-separated integers)',
- 'caching_admin_kinds' => 'Admin kinds (* for all, or comma-separated)',
- 'caching_live_enabled' => 'Live subscription enabled (true/false)',
- 'caching_backfill_enabled' => 'Backfill enabled (true/false)',
- 'caching_backfill_page_size' => 'Backfill page size (events per tick)',
- 'caching_backfill_tick_interval_ms' => 'Backfill tick interval (ms)',
- 'caching_follow_graph_refresh_seconds' => 'Follow graph refresh interval (seconds)',
- 'caching_relay_discovery_refresh_seconds' => 'Relay discovery refresh (seconds)',
- 'caching_max_followed_pubkeys' => 'Max followed pubkeys',
- 'caching_max_upstream_relays' => 'Max upstream relays',
- 'caching_max_relays_per_pubkey' => 'Max relays per pubkey',
- 'caching_query_timeout_ms' => 'Query timeout (ms)',
- 'caching_live_resubscribe_seconds' => 'Live resubscribe interval (seconds)',
-];
-
-$values = [];
-foreach (array_keys($config_keys) as $key) {
- $stmt = $pdo->prepare("SELECT value FROM config WHERE key = ?");
- $stmt->execute([$key]);
- $values[$key] = $stmt->fetchColumn() ?: '';
-}
-
-admin_header('config', 'C-Relay-PG Admin — Config');
-?>
-
-
-
-
-
-
✓ = e($message) ?>
-
-
-
✗ = e($error) ?>
-
-
-
-
-
- Saving bumps caching_config_generation so the caching service
- detects the change and hot-reloads. No relay restart needed.
-
-
-
-
diff --git a/admin/follows.php b/admin/follows.php
deleted file mode 100644
index 3c6e390..0000000
--- a/admin/follows.php
+++ /dev/null
@@ -1,153 +0,0 @@
->\'name\' ILIKE ? OR e.content::json->>\'display_name\' ILIKE ?)';
- $params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%";
-}
-$where_sql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
-
-$sort_map = [
- 'events' => 'fp.events_fetched DESC',
- 'name' => "COALESCE(e.content::json->>'display_name', e.content::json->>'name') ASC NULLS LAST",
- 'recent' => 'fp.last_seen DESC',
- 'complete' => 'fp.backfill_complete ASC, fp.events_fetched DESC',
-];
-$sort_sql = $sort_map[$sort] ?? $sort_map['events'];
-
-$stmt = $pdo->prepare("SELECT COUNT(*) FROM caching_followed_pubkeys fp LEFT JOIN LATERAL (SELECT content FROM events WHERE pubkey = fp.pubkey AND kind = 0 ORDER BY created_at DESC LIMIT 1) e ON true $where_sql");
-$stmt->execute($params);
-$total = intval($stmt->fetchColumn());
-
-$sql = "
- SELECT fp.pubkey, fp.is_root, fp.backfill_complete, fp.events_fetched,
- fp.first_seen, fp.last_seen,
- e.content::json->>'name' AS name,
- e.content::json->>'display_name' AS display_name,
- e.content::json->>'picture' AS picture,
- e.content::json->>'nip05' AS nip05,
- (SELECT COUNT(*) FROM events WHERE pubkey = fp.pubkey) AS total_events
- FROM caching_followed_pubkeys fp
- LEFT JOIN LATERAL (SELECT content FROM events WHERE pubkey = fp.pubkey AND kind = 0 ORDER BY created_at DESC LIMIT 1) e ON true
- $where_sql
- ORDER BY $sort_sql
- LIMIT $per OFFSET $offset
-";
-$stmt = $pdo->prepare($sql);
-$stmt->execute($params);
-$follows = $stmt->fetchAll();
-
-admin_header('follows', 'C-Relay-PG Admin — Follows');
-?>
-
-
-
-
-
-
-
-
-
-
- Name
- npub
- Root?
- Events in DB
- Backfill
- Last Seen
- Relays
-
-
-
- 0, 'incomplete' => 0, 'errors' => 0];
- try {
- $relay_count = $pdo->prepare("SELECT COUNT(*) AS total, COUNT(*) FILTER (WHERE complete = false) AS incomplete, COUNT(*) FILTER (WHERE last_status LIKE 'error%') AS errors FROM caching_backfill_relay_progress WHERE author_pubkey = ?");
- $relay_count->execute([$f['pubkey']]);
- $rc = $relay_count->fetch() ?: $rc;
- } catch (PDOException $ex) {}
- ?>
-
-
-
- = e($name) ?>
-
- ✓ = e($f['nip05']) ?>
-
-
- unknown
-
-
- = e(trunc($npub, 22)) ?>
- = $f['is_root'] ? 'root ' : '' ?>
- = number_format(intval($f['total_events'])) ?>
- = $f['backfill_complete']
- ? '✓ complete '
- : 'in progress ' ?>
- = time_ago(intval($f['last_seen'])) ?>
-
- 0): ?>
- = intval($rc['total']) ?> relays (= intval($rc['incomplete']) ?> incomplete
- 0): ?>
- , = intval($rc['errors']) ?> errors
- )
- View relay details →
-
- —
-
-
-
-
-
- No followed pubkeys found.
-
-
-
-
-
- = pagination($page, $per, $total, 'follows.php' . ($filter !== 'all' ? '?filter=' . e($filter) : '') . ($search ? '&search=' . e($search) : '') . ($sort !== 'events' ? '&sort=' . e($sort) : '')) ?>
-
-
-
diff --git a/admin/inbox.php b/admin/inbox.php
deleted file mode 100644
index 901a617..0000000
--- a/admin/inbox.php
+++ /dev/null
@@ -1,111 +0,0 @@
-query("
- SELECT
- COUNT(*) AS pending,
- COUNT(*) FILTER (WHERE source_class = 'live') AS live,
- COUNT(*) FILTER (WHERE source_class = 'backfill') AS backfill,
- COUNT(*) FILTER (WHERE source_class = 'discovery') AS discovery,
- COUNT(*) FILTER (WHERE priority = 0) AS high_priority,
- COALESCE(EXTRACT(EPOCH FROM NOW())::BIGINT - MIN(received_at), 0) AS oldest_age
- FROM caching_event_inbox
-")->fetch() ?: ['pending' => 0, 'live' => 0, 'backfill' => 0, 'discovery' => 0, 'high_priority' => 0, 'oldest_age' => 0];
-
-$recent = $pdo->query("
- SELECT event_id, event_json->>'pubkey' AS pubkey, event_json->>'kind' AS kind,
- source_relay, source_class, priority, received_at
- FROM caching_event_inbox
- ORDER BY received_at DESC
- LIMIT 20
-")->fetchAll();
-
-admin_header('inbox', 'C-Relay-PG Admin — Inbox');
-?>
-
-
-
-
-
-
-
Pending Events
-
= number_format(intval($stats['pending'])) ?>
-
= intval($stats['live']) ?> live, = intval($stats['backfill']) ?> backfill, = intval($stats['discovery']) ?> discovery
-
-
-
High Priority
-
= number_format(intval($stats['high_priority'])) ?>
-
priority = 0 (live events)
-
-
-
Oldest Pending
-
= intval($stats['oldest_age']) ?>s
-
age of oldest event in queue
-
-
-
- 300): ?>
-
- ⚠ Oldest pending event is = intval($stats['oldest_age']) ?>s old — the inbox poller may not be keeping up.
- Check that caching_inbox_enabled = true in the config table and the relay is running.
-
-
-
-
-
-
-
-
-
-
- Event ID
- Pubkey
- Kind
- Source Relay
- Class
- Priority
- Received
-
-
-
-
-
- = e(trunc($r['event_id'], 16)) ?>
- = e(trunc($r['pubkey'], 16)) ?>
- = e($r['kind']) ?>
- = e($r['source_relay'] ?? '—') ?>
- = e($r['source_class']) ?>
- = intval($r['priority']) ?>
- = time_ago(intval($r['received_at'])) ?>
-
-
-
- Inbox is empty — all events have been dequeued.
-
-
-
-
-
-
-
-
-
diff --git a/admin/index.php b/admin/index.php
index 29a6715..ebb6b27 100644
--- a/admin/index.php
+++ b/admin/index.php
@@ -1,158 +1,429 @@
query("SELECT * FROM caching_service_state WHERE id = 1")->fetch() ?: [];
-
-// Active backfill target (table may not exist yet on fresh start)
-$active = ['active_pubkey' => '', 'active_relay' => ''];
+// Fetch relay info for the header (from config table)
+$relay_name = '';
+$relay_desc = '';
+$relay_pubkey = '';
+$relay_version = '';
try {
- $active = $pdo->query("SELECT active_pubkey, active_relay FROM caching_backfill_active WHERE id = 1")->fetch() ?: $active;
+ $relay_name = $pdo->query("SELECT value FROM config WHERE key = 'relay_name'")->fetchColumn() ?: 'C-Relay-PG';
+ $relay_desc = $pdo->query("SELECT value FROM config WHERE key = 'relay_description'")->fetchColumn() ?: '';
+ $relay_pubkey = $pdo->query("SELECT value FROM config WHERE key = 'relay_pubkey'")->fetchColumn() ?: '';
+ $relay_version = $pdo->query("SELECT value FROM config WHERE key = 'relay_version'")->fetchColumn() ?: '';
} catch (PDOException $e) {}
-// Error relay summary
-$error_stats = ['auto_completed' => 0, 'error_count' => 0, 'timeout_count' => 0, 'incomplete' => 0, 'total' => 0];
-try {
- $error_stats = $pdo->query("
- SELECT
- COUNT(*) FILTER (WHERE consecutive_errors >= 3) AS auto_completed,
- COUNT(*) FILTER (WHERE last_status LIKE 'error%') AS error_count,
- COUNT(*) FILTER (WHERE last_status = 'timeout') AS timeout_count,
- COUNT(*) FILTER (WHERE complete = false) AS incomplete,
- COUNT(*) AS total
- FROM caching_backfill_relay_progress
- ")->fetch() ?: $error_stats;
-} catch (PDOException $e) {}
-
-// Inbox stats
-$inbox = $pdo->query("
- SELECT
- COUNT(*) AS pending,
- COUNT(*) FILTER (WHERE source_class = 'live') AS live,
- COUNT(*) FILTER (WHERE source_class = 'backfill') AS backfill,
- COALESCE(EXTRACT(EPOCH FROM NOW())::BIGINT - MIN(received_at), 0) AS oldest_age
- FROM caching_event_inbox
-")->fetch() ?: ['pending' => 0, 'live' => 0, 'backfill' => 0, 'oldest_age' => 0];
-
-// Event counts
-$event_stats = $pdo->query("SELECT COUNT(*) AS total, COUNT(DISTINCT pubkey) AS authors FROM events")->fetch() ?: ['total' => 0, 'authors' => 0];
-$kind_stats = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 10")->fetchAll();
-
-admin_header('dashboard', 'C-Relay-PG Admin — Dashboard');
-?>
-
-
-
-
-
-
Service State
-
= e($state['service_state'] ?? 'unknown') ?>
-
Heartbeat: = time_ago(intval($state['heartbeat_at'] ?? 0)) ?>
-
-
-
Followed Authors
-
= intval($state['followed_author_count'] ?? 0) ?>
-
= intval($state['selected_relay_count'] ?? 0) ?> relays, = intval($state['connected_relay_count'] ?? 0) ?> connected
-
-
-
Backfill Progress
-
= intval($state['backfill_authors_complete'] ?? 0) ?> / = intval($state['backfill_authors_total'] ?? 0) ?>
-
= ($state['backfill_authors_total'] ?? 0) > 0 ? round(intval($state['backfill_authors_complete'] ?? 0) / intval($state['backfill_authors_total']) * 100) : 0 ?>% complete
-
-
-
Events Fetched
-
= number_format(intval($state['events_fetched'] ?? 0)) ?>
-
= number_format(intval($state['inbox_inserts'] ?? 0)) ?> inbox inserts
-
-
-
Events in DB
-
= number_format(intval($event_stats['total'] ?? 0)) ?>
-
= number_format(intval($event_stats['authors'] ?? 0)) ?> authors
-
-
-
Inbox Pending
-
= number_format(intval($inbox['pending'] ?? 0)) ?>
-
= intval($inbox['live'] ?? 0) ?> live, = intval($inbox['backfill'] ?? 0) ?> backfill
-
-
-
Error Relays
-
= intval($error_stats['error_count'] ?? 0) ?>
-
= intval($error_stats['auto_completed'] ?? 0) ?> auto-completed
-
-
-
Config Generation
-
= intval($state['config_generation'] ?? 0) ?>
-
Updated = time_ago(intval($state['updated_at'] ?? 0)) ?>
-
-
-
-
-
-
-
-
-
⚡ Working on: = e(trunc($active['active_pubkey'], 16)) ?> @ = e($active['active_relay'] ?? '') ?>
-
-
✓ Caching complete — no active backfill target
-
-
-
-
-
-
-
-
- Kind Count
-
-
- = intval($ks['kind']) ?> = number_format(intval($ks['cnt'])) ?>
-
-
-
-
-
-
-
-
+// Format npub (63 chars) into 3 lines of "xxxxxxx xxxxxxx xxxxxxx"
+$formatted_npub = $relay_npub;
+if (strlen($relay_npub) === 63) {
+ $line1 = substr($relay_npub, 0, 7) . ' ' . substr($relay_npub, 7, 7) . ' ' . substr($relay_npub, 14, 7);
+ $line2 = substr($relay_npub, 21, 7) . ' ' . substr($relay_npub, 28, 7) . ' ' . substr($relay_npub, 35, 7);
+ $line3 = substr($relay_npub, 42, 7) . ' ' . substr($relay_npub, 49, 7) . ' ' . substr($relay_npub, 56, 7);
+ $formatted_npub = $line1 . "\n" . $line2 . "\n" . $line3;
+}
+$display_name = $relay_version ? ($relay_name . ' ' . $relay_version) : $relay_name;
+?>
+
+
+
+
+
+
+ C-Relay-PG Admin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
⛔
+
ACCESS DENIED
+
This interface is restricted to the relay administrator.
+
The logged-in account does not have admin privileges.
+
LOGOUT
+
+
+
+
+
+
+
+
+
+ 1H
+ 1D
+ 1M
+ 1Y
+
+
Loading chart...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parameter Value Actions
+
+ Loading...
+
+
+
+
+ REFRESH
+
+
+
+
+
+
+
+
+
+ Rule Type Pattern Type Pattern Value Status Actions
+
+ Loading...
+
+
+
+
+
+
+
+
+
+
+ Admin Contact List (kind 3):
+ Loading...
+
+
+
WoT Level:
+
+ OFF
+ WRITE ONLY
+ FULL
+
+
Loading...
+
+
+ Whitelisted Pubkeys:
+ —
+
+
+ SYNC FROM KIND 3
+ REFRESH STATUS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ REFRESH
+
+
+
+
+
+
+
+ SQL Query (SELECT only):
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/lib/ascii_chart.php b/admin/lib/ascii_chart.php
new file mode 100644
index 0000000..bb93593
--- /dev/null
+++ b/admin/lib/ascii_chart.php
@@ -0,0 +1,147 @@
+
+ * injection (white-space: pre) and terminal display via curl.
+ */
+
+/**
+ * Render an ASCII bar chart from an array of bin counts.
+ *
+ * @param array $bins Array of integer counts (one per time bin, left=oldest)
+ * @param array $options {
+ * @var string $title Chart title (centered at top)
+ * @var int $max_height Chart height in rows (default 11)
+ * @var string $x_axis_label Label below the X-axis (default '')
+ * @var int $bin_duration Seconds per bin — for X-axis elapsed labels
+ * @var int $label_interval Label every N bins (default 5)
+ * }
+ * @return string The ASCII chart as a multi-line string.
+ */
+function render_ascii_chart(array $bins, array $options = []): string {
+ $title = $options['title'] ?? 'New Events';
+ $max_height = $options['max_height'] ?? 11;
+ $x_axis_label = $options['x_axis_label'] ?? '';
+ $bin_duration = $options['bin_duration'] ?? 10;
+ $label_interval = $options['label_interval'] ?? 5;
+
+ $num_bins = count($bins);
+ if ($num_bins === 0) {
+ return "No data available.\n";
+ }
+
+ $max_count = max($bins);
+ if ($max_count < 1) $max_count = 1; // Avoid division by zero for all-empty bins
+
+ // Scaling: each X represents scale_factor counts
+ $scale_factor = max(1, (int)ceil($max_count / $max_height));
+ $scaled_max = (int)ceil($max_count / $scale_factor) * $scale_factor;
+
+ $output = '';
+
+ // --- Title (centered) ---
+ $chart_width = 4 + $num_bins; // 4 = Y-axis number width (3) + separator (1)
+ if ($title !== '') {
+ $title_padding = (int)floor(($chart_width - strlen($title)) / 2);
+ if ($title_padding < 0) $title_padding = 0;
+ $output .= str_repeat(' ', $title_padding) . $title . "\n\n";
+ }
+
+ // --- Bar rows (top to bottom) ---
+ for ($row = $max_height; $row > 0; $row--) {
+ $row_count = ($row - 1) * $scale_factor + 1;
+ $line = str_pad((string)$row_count, 3, ' ', STR_PAD_LEFT) . ' |';
+
+ for ($i = 0; $i < $num_bins; $i++) {
+ $count = $bins[$i];
+ $scaled_height = ($count > 0) ? (int)ceil($count / $scale_factor) : 0;
+ $line .= ($scaled_height >= $row) ? 'X' : ' ';
+ }
+ $output .= $line . "\n";
+ }
+
+ // --- X-axis line ---
+ $output .= ' +' . str_repeat('-', $num_bins) . "\n";
+
+ // --- X-axis labels (elapsed time every label_interval bins) ---
+ $label_line = ' ';
+ $labels = [];
+ for ($i = 0; $i < $num_bins; $i++) {
+ if ($i % $label_interval === 0) {
+ $elapsed_sec = $i * $bin_duration;
+ $labels[] = format_elapsed_time($elapsed_sec);
+ }
+ }
+ // Build label line with spacing
+ for ($i = 0; $i < count($labels); $i++) {
+ $label_line .= $labels[$i];
+ if ($i < count($labels) - 1) {
+ $spacing = $label_interval - strlen($labels[$i]);
+ if ($spacing < 1) $spacing = 1;
+ $label_line .= str_repeat(' ', $spacing);
+ }
+ }
+ // Pad to match X-axis dash line length
+ $min_label_len = 4 + $num_bins;
+ if (strlen($label_line) < $min_label_len) {
+ $label_line .= str_repeat(' ', $min_label_len - strlen($label_line));
+ }
+ $output .= $label_line . "\n";
+
+ // --- X-axis label (if provided) ---
+ if ($x_axis_label !== '') {
+ $label_pad = (int)floor(($num_bins - strlen($x_axis_label)) / 2);
+ if ($label_pad < 0) $label_pad = 0;
+ $output .= "\n" . ' ' . str_repeat(' ', $label_pad) . $x_axis_label . "\n";
+ }
+
+ return $output;
+}
+
+/**
+ * Format an elapsed time in seconds as a compact label.
+ * < 60s → "Ns"
+ * < 1h → "Nm"
+ * < 1d → "Nh"
+ * else → "Nd"
+ */
+function format_elapsed_time(int $seconds): string {
+ if ($seconds < 60) {
+ return $seconds . 's';
+ } elseif ($seconds < 3600) {
+ return (int)floor($seconds / 60) . 'm';
+ } elseif ($seconds < 86400) {
+ return (int)floor($seconds / 3600) . 'h';
+ } else {
+ return (int)floor($seconds / 86400) . 'd';
+ }
+}
+
+/**
+ * Build a fixed-length bin array from SQL query rows.
+ *
+ * The query returns only non-empty bins. This function creates a
+ * zero-filled array of $num_bins length and overlays the counts
+ * at the correct positions, so empty time slots show as blank
+ * columns — the chart always advances in time.
+ *
+ * @param array $rows Query rows with 'bin' (int) and 'cnt' (int)
+ * @param int $num_bins Total number of bins (fixed length)
+ * @return array Zero-filled array of counts
+ */
+function build_bin_array(array $rows, int $num_bins): array {
+ $bins = array_fill(0, $num_bins, 0);
+ foreach ($rows as $r) {
+ $idx = (int)$r['bin'];
+ if ($idx >= 0 && $idx < $num_bins) {
+ $bins[$idx] = (int)$r['cnt'];
+ }
+ }
+ return $bins;
+}
diff --git a/admin/lib/db.php b/admin/lib/db.php
index 8c6de96..85afaef 100644
--- a/admin/lib/db.php
+++ b/admin/lib/db.php
@@ -12,14 +12,18 @@ function db(): PDO {
static $pdo = null;
if ($pdo === null) {
$cfg = require __DIR__ . '/config.php';
+ // Build DSN — supports both TCP (host=localhost) and Unix socket
+ // (host=/var/run/postgresql). Omit password if empty (peer auth).
$dsn = sprintf(
- 'pgsql:host=%s;port=%d;dbname=%s;user=%s;password=%s',
+ 'pgsql:host=%s;port=%d;dbname=%s;user=%s',
$cfg['db_host'],
$cfg['db_port'],
$cfg['db_name'],
- $cfg['db_user'],
- $cfg['db_password']
+ $cfg['db_user']
);
+ if (!empty($cfg['db_password'])) {
+ $dsn .= ';password=' . $cfg['db_password'];
+ }
try {
$pdo = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
diff --git a/admin/lib/helpers.php b/admin/lib/helpers.php
index 032366c..1938676 100644
--- a/admin/lib/helpers.php
+++ b/admin/lib/helpers.php
@@ -126,6 +126,62 @@ function query_param(string $key, $default = null) {
return $_GET[$key] ?? $default;
}
+/**
+ * Batch-resolve profiles from the profiles cache table.
+ * Returns [pubkey_hex => ['name'=>..., 'display_name'=>...,
+ * 'best_name'=>..., 'picture'=>..., 'nip05'=>...]].
+ * Pubkeys with no cached profile are absent from the result.
+ */
+function profile_map(array $pubkeys): array {
+ if (empty($pubkeys)) return [];
+ $pdo = db();
+ $map = [];
+ try {
+ // Build parameterized IN clause: WHERE pubkey = ANY(?)
+ // PDO PostgreSQL supports passing an array as a string literal.
+ $placeholders = implode(',', array_fill(0, count($pubkeys), '?'));
+ $stmt = $pdo->prepare(
+ "SELECT pubkey, name, display_name, picture, nip05 FROM profiles "
+ . "WHERE pubkey IN ($placeholders)"
+ );
+ $stmt->execute(array_values($pubkeys));
+ $rows = $stmt->fetchAll();
+ foreach ($rows as $r) {
+ $map[$r['pubkey']] = [
+ 'name' => $r['name'] ?? '',
+ 'display_name' => $r['display_name'] ?? '',
+ 'best_name' => profile_display_name($r),
+ 'picture' => $r['picture'] ?? '',
+ 'nip05' => $r['nip05'] ?? '',
+ ];
+ }
+ } catch (PDOException $e) {}
+ return $map;
+}
+
+/**
+ * Apply the profile_name_preference config key to resolve the display name.
+ * Never returns null. Returns '' if neither field is set.
+ */
+function profile_display_name(array $profile): string {
+ $name = $profile['name'] ?? '';
+ $display_name = $profile['display_name'] ?? '';
+ static $pref = null;
+ if ($pref === null) {
+ try {
+ $pdo = db();
+ $pref = $pdo->query("SELECT value FROM config WHERE key = 'profile_name_preference'")->fetchColumn() ?: 'display_name';
+ } catch (PDOException $e) {
+ $pref = 'display_name';
+ }
+ }
+ if ($pref === 'name') {
+ return $name !== '' ? $name : $display_name;
+ }
+ // Default: prefer display_name, fall back to name.
+ return $display_name !== '' ? $display_name : $name;
+}
+
/**
* Render the shared HTML header with side navigation.
* Pass the active page name to highlight the current nav item.
diff --git a/admin/php_server.log b/admin/php_server.log
new file mode 100644
index 0000000..601dac6
--- /dev/null
+++ b/admin/php_server.log
@@ -0,0 +1,8754 @@
+[Thu Jul 30 15:56:32 2026] PHP 8.4.23 Development Server (http://127.0.0.1:8088) started
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56782 Accepted
+[Thu Jul 30 15:56:36 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56782 [200]: GET /api/stats.php
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56782 Closing
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56792 Accepted
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56792 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:56:36 2026] 127.0.0.1:56792 Closing
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49262 Accepted
+[Thu Jul 30 15:56:46 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49262 [200]: GET /api/stats.php
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49262 Closing
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49266 Accepted
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49266 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:56:46 2026] 127.0.0.1:49266 Closing
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58436 Accepted
+[Thu Jul 30 15:56:56 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58436 [200]: GET /api/stats.php
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58436 Closing
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58440 Accepted
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58440 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:56:56 2026] 127.0.0.1:58440 Closing
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58450 Accepted
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58450 [200]: GET /
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58450 Closing
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58460 Accepted
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58460 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58460 Closing
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58472 Accepted
+[Thu Jul 30 15:56:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58472 [200]: GET /api/stats.php
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58472 Closing
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58486 Accepted
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58486 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:56:58 2026] 127.0.0.1:58486 Closing
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42862 Accepted
+[Thu Jul 30 15:57:06 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42862 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42862 Closing
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42868 Accepted
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42868 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:06 2026] 127.0.0.1:42868 Closing
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39444 Accepted
+[Thu Jul 30 15:57:16 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39444 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39444 Closing
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39458 Accepted
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39458 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:16 2026] 127.0.0.1:39458 Closing
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37650 Accepted
+[Thu Jul 30 15:57:26 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37650 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37650 Closing
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37666 Accepted
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37666 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:26 2026] 127.0.0.1:37666 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37680 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37680 [200]: GET /
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37680 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37682 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37696 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37700 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37710 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37682 [200]: GET /assets/index.css
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37696 [200]: GET /assets/nostr.bundle.js
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37700 [200]: GET /assets/nostr-lite.js
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37710 [200]: GET /assets/app.js
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37682 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37710 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37700 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37696 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37716 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37716 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37716 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37726 Accepted
+[Thu Jul 30 15:57:28 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37726 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37726 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37740 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37752 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37740 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37740 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37754 Accepted
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37752 [200]: GET /favicon.ico
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37752 Closing
+[Thu Jul 30 15:57:28 2026] 127.0.0.1:37758 Accepted
+[Thu Jul 30 15:57:28 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37754 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37754 Closing
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37758 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37758 Closing
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37760 Accepted
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37760 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:29 2026] 127.0.0.1:37760 Closing
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51134 Accepted
+[Thu Jul 30 15:57:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51134 [200]: GET /api/stats.php
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51134 Closing
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51146 Accepted
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51146 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 15:57:38 2026] 127.0.0.1:51146 Closing
+[Thu Jul 30 15:57:39 2026] 127.0.0.1:51162 Accepted
+[Thu Jul 30 15:57:39 2026] 127.0.0.1:51162 [200]: GET /api/subscriptions.php
+[Thu Jul 30 15:57:39 2026] 127.0.0.1:51162 Closing
+[Thu Jul 30 15:57:42 2026] 127.0.0.1:51178 Accepted
+[Thu Jul 30 15:57:42 2026] 127.0.0.1:51178 [200]: GET /api/caching.php
+[Thu Jul 30 15:57:42 2026] 127.0.0.1:51178 Closing
+[Thu Jul 30 16:32:18 2026] 127.0.0.1:56610 Accepted
+[Thu Jul 30 16:32:18 2026] 127.0.0.1:56610 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 16:32:18 2026] 127.0.0.1:56610 Closing
+[Thu Jul 30 16:33:01 2026] 127.0.0.1:45180 Accepted
+[Thu Jul 30 16:33:01 2026] 127.0.0.1:45180 [200]: GET /api/chart.php?range=month
+[Thu Jul 30 16:33:01 2026] 127.0.0.1:45180 Closing
+[Thu Jul 30 16:33:08 2026] 127.0.0.1:52132 Accepted
+[Thu Jul 30 16:33:08 2026] 127.0.0.1:52132 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 16:33:08 2026] 127.0.0.1:52132 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32860 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32860 [200]: GET /
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32860 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32864 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32864 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32864 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32874 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32874 [200]: GET /assets/index.css
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32874 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32888 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32888 [200]: GET /assets/nostr.bundle.js
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32900 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32912 Accepted
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32888 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32900 [200]: GET /assets/nostr-lite.js
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32912 [200]: GET /assets/app.js
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32900 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32912 Closing
+[Thu Jul 30 16:47:03 2026] 127.0.0.1:32920 Accepted
+[Thu Jul 30 16:47:04 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32920 [200]: GET /api/stats.php
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32920 Closing
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32922 Accepted
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32934 Accepted
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32922 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32922 Closing
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32940 Accepted
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32934 [200]: GET /favicon.ico
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32934 Closing
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32942 Accepted
+[Thu Jul 30 16:47:04 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32940 [200]: GET /api/stats.php
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32940 Closing
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32942 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32942 Closing
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32946 Accepted
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32946 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:47:04 2026] 127.0.0.1:32946 Closing
+[Thu Jul 30 16:47:05 2026] 127.0.0.1:32962 Accepted
+[Thu Jul 30 16:47:05 2026] 127.0.0.1:32962 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Thu Jul 30 16:47:05 2026] 127.0.0.1:32962 Closing
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44000 Accepted
+[Thu Jul 30 16:47:13 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44000 [200]: GET /api/stats.php
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44000 Closing
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44016 Accepted
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44016 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:47:13 2026] 127.0.0.1:44016 Closing
+[Thu Jul 30 16:47:18 2026] 127.0.0.1:38856 Accepted
+[Thu Jul 30 16:47:18 2026] 127.0.0.1:38856 [200]: GET /api/events.php?limit=50
+[Thu Jul 30 16:47:18 2026] 127.0.0.1:38856 Closing
+[Thu Jul 30 16:48:49 2026] 127.0.0.1:36120 Accepted
+[Thu Jul 30 16:48:49 2026] 127.0.0.1:36120 [200]: GET /api/caching.php
+[Thu Jul 30 16:48:49 2026] 127.0.0.1:36120 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39304 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39304 [200]: GET /
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39304 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39312 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39312 [200]: GET /assets/index.css
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39312 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39322 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39324 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39322 [200]: GET /assets/nostr.bundle.js
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39324 [200]: GET /assets/nostr-lite.js
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39322 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39324 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39338 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39338 [200]: GET /assets/app.js
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39338 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39344 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39344 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39344 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39354 Accepted
+[Thu Jul 30 16:49:17 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39354 [200]: GET /api/stats.php
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39354 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39370 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39380 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39370 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39370 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39394 Accepted
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39380 [200]: GET /favicon.ico
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39380 Closing
+[Thu Jul 30 16:49:17 2026] 127.0.0.1:39406 Accepted
+[Thu Jul 30 16:49:17 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39394 [200]: GET /api/stats.php
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39394 Closing
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39406 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39406 Closing
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39412 Accepted
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39412 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39412 Closing
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39426 Accepted
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39426 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 16:49:18 2026] 127.0.0.1:39426 Closing
+[Thu Jul 30 16:49:23 2026] 127.0.0.1:39436 Accepted
+[Thu Jul 30 16:49:23 2026] 127.0.0.1:39436 [200]: GET /api/events.php?limit=50
+[Thu Jul 30 16:49:23 2026] 127.0.0.1:39436 Closing
+[Thu Jul 30 16:49:42 2026] 127.0.0.1:59184 Accepted
+[Thu Jul 30 16:49:42 2026] 127.0.0.1:59184 [200]: GET /api/caching.php
+[Thu Jul 30 16:49:42 2026] 127.0.0.1:59184 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40002 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40002 [200]: GET /
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40002 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40006 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40006 [200]: GET /assets/index.css
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40006 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40010 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40020 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40030 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40010 [200]: GET /assets/nostr.bundle.js
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40020 [200]: GET /assets/nostr-lite.js
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40030 [200]: GET /assets/app.js
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40030 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40010 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40020 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40038 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40052 Accepted
+[Thu Jul 30 17:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40038 [200]: GET /api/stats.php
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40038 Closing
+[Thu Jul 30 17:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40052 [200]: GET /api/stats.php
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40052 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40068 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40076 Accepted
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40068 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40068 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40076 [200]: GET /favicon.ico
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40076 Closing
+[Thu Jul 30 17:48:32 2026] 127.0.0.1:40084 Accepted
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40084 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40084 Closing
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40088 Accepted
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40088 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40088 Closing
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40090 Accepted
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40090 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Thu Jul 30 17:48:33 2026] 127.0.0.1:40090 Closing
+[Thu Jul 30 17:48:35 2026] 127.0.0.1:40092 Accepted
+[Thu Jul 30 17:48:35 2026] 127.0.0.1:40092 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 17:48:35 2026] 127.0.0.1:40092 Closing
+[Thu Jul 30 17:48:37 2026] 127.0.0.1:52116 Accepted
+[Thu Jul 30 17:48:37 2026] 127.0.0.1:52116 [200]: GET /api/chart.php?range=month
+[Thu Jul 30 17:48:37 2026] 127.0.0.1:52116 Closing
+[Thu Jul 30 17:48:40 2026] 127.0.0.1:52120 Accepted
+[Thu Jul 30 17:48:40 2026] 127.0.0.1:52120 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 17:48:40 2026] 127.0.0.1:52120 Closing
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52128 Accepted
+[Thu Jul 30 17:48:42 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52128 [200]: GET /api/stats.php
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52128 Closing
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52144 Accepted
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52144 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 17:48:42 2026] 127.0.0.1:52144 Closing
+[Thu Jul 30 17:48:43 2026] 127.0.0.1:52160 Accepted
+[Thu Jul 30 17:48:43 2026] 127.0.0.1:52160 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 17:48:43 2026] 127.0.0.1:52160 Closing
+[Thu Jul 30 17:48:44 2026] 127.0.0.1:52164 Accepted
+[Thu Jul 30 17:48:44 2026] 127.0.0.1:52164 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 17:48:44 2026] 127.0.0.1:52164 Closing
+[Thu Jul 30 17:48:46 2026] 127.0.0.1:52178 Accepted
+[Thu Jul 30 17:48:46 2026] 127.0.0.1:52178 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 17:48:46 2026] 127.0.0.1:52178 Closing
+[Thu Jul 30 17:48:48 2026] 127.0.0.1:45848 Accepted
+[Thu Jul 30 17:48:48 2026] 127.0.0.1:45848 [200]: GET /api/caching.php
+[Thu Jul 30 17:48:48 2026] 127.0.0.1:45848 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49216 Accepted
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49216 [200]: GET /
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49216 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49220 Accepted
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49220 [200]: GET /assets/index.css
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49234 Accepted
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49248 Accepted
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49220 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49264 Accepted
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49234 [200]: GET /assets/nostr.bundle.js
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49264 [200]: GET /assets/app.js
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49248 [200]: GET /assets/nostr-lite.js
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49264 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49234 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49248 Closing
+[Thu Jul 30 18:40:01 2026] 127.0.0.1:49270 Accepted
+[Thu Jul 30 18:40:03 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49270 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49270 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49284 Accepted
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49292 Accepted
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49284 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49284 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49294 Accepted
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49292 [200]: GET /favicon.ico
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49292 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49308 Accepted
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49294 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49294 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49318 Accepted
+[Thu Jul 30 18:40:03 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49308 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49308 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49318 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49318 Closing
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49326 Accepted
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49326 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 18:40:03 2026] 127.0.0.1:49326 Closing
+[Thu Jul 30 18:40:05 2026] 127.0.0.1:49328 Accepted
+[Thu Jul 30 18:40:05 2026] 127.0.0.1:49328 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 18:40:05 2026] 127.0.0.1:49328 Closing
+[Thu Jul 30 18:40:06 2026] 127.0.0.1:55346 Accepted
+[Thu Jul 30 18:40:06 2026] 127.0.0.1:55346 [200]: GET /api/chart.php?range=month
+[Thu Jul 30 18:40:06 2026] 127.0.0.1:55346 Closing
+[Thu Jul 30 18:40:07 2026] 127.0.0.1:55362 Accepted
+[Thu Jul 30 18:40:07 2026] 127.0.0.1:55362 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:07 2026] 127.0.0.1:55362 Closing
+[Thu Jul 30 18:40:08 2026] 127.0.0.1:55366 Accepted
+[Thu Jul 30 18:40:08 2026] 127.0.0.1:55366 [200]: GET /api/chart.php?range=month
+[Thu Jul 30 18:40:08 2026] 127.0.0.1:55366 Closing
+[Thu Jul 30 18:40:09 2026] 127.0.0.1:55368 Accepted
+[Thu Jul 30 18:40:09 2026] 127.0.0.1:55368 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 18:40:09 2026] 127.0.0.1:55368 Closing
+[Thu Jul 30 18:40:10 2026] 127.0.0.1:55374 Accepted
+[Thu Jul 30 18:40:10 2026] 127.0.0.1:55374 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 18:40:10 2026] 127.0.0.1:55374 Closing
+[Thu Jul 30 18:40:11 2026] 127.0.0.1:55388 Accepted
+[Thu Jul 30 18:40:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:11 2026] 127.0.0.1:55388 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:11 2026] 127.0.0.1:55388 Closing
+[Thu Jul 30 18:40:11 2026] 127.0.0.1:55400 Accepted
+[Thu Jul 30 18:40:12 2026] 127.0.0.1:55400 [200]: GET /api/chart.php?range=hour
+[Thu Jul 30 18:40:12 2026] 127.0.0.1:55400 Closing
+[Thu Jul 30 18:40:13 2026] 127.0.0.1:55406 Accepted
+[Thu Jul 30 18:40:13 2026] 127.0.0.1:55406 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 18:40:13 2026] 127.0.0.1:55406 Closing
+[Thu Jul 30 18:40:14 2026] 127.0.0.1:55410 Accepted
+[Thu Jul 30 18:40:14 2026] 127.0.0.1:55410 [200]: GET /api/chart.php?range=month
+[Thu Jul 30 18:40:14 2026] 127.0.0.1:55410 Closing
+[Thu Jul 30 18:40:15 2026] 127.0.0.1:55426 Accepted
+[Thu Jul 30 18:40:15 2026] 127.0.0.1:55426 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:15 2026] 127.0.0.1:55426 Closing
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54046 Accepted
+[Thu Jul 30 18:40:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54046 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54046 Closing
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54062 Accepted
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54062 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:21 2026] 127.0.0.1:54062 Closing
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41238 Accepted
+[Thu Jul 30 18:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41238 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41238 Closing
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41250 Accepted
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41250 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:31 2026] 127.0.0.1:41250 Closing
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54792 Accepted
+[Thu Jul 30 18:40:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54792 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54792 Closing
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54794 Accepted
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54794 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:41 2026] 127.0.0.1:54794 Closing
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37532 Accepted
+[Thu Jul 30 18:40:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37532 [200]: GET /api/stats.php
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37532 Closing
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37548 Accepted
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37548 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:40:51 2026] 127.0.0.1:37548 Closing
+[Thu Jul 30 18:41:01 2026] 127.0.0.1:41054 Accepted
+[Thu Jul 30 18:41:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:02 2026] 127.0.0.1:41054 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:02 2026] 127.0.0.1:41054 Closing
+[Thu Jul 30 18:41:02 2026] 127.0.0.1:41066 Accepted
+[Thu Jul 30 18:41:02 2026] 127.0.0.1:41066 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:02 2026] 127.0.0.1:41066 Closing
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51786 Accepted
+[Thu Jul 30 18:41:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51786 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51786 Closing
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51798 Accepted
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51798 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:11 2026] 127.0.0.1:51798 Closing
+[Thu Jul 30 18:41:21 2026] 127.0.0.1:50828 Accepted
+[Thu Jul 30 18:41:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:22 2026] 127.0.0.1:50828 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:22 2026] 127.0.0.1:50828 Closing
+[Thu Jul 30 18:41:22 2026] 127.0.0.1:50832 Accepted
+[Thu Jul 30 18:41:22 2026] 127.0.0.1:50832 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:22 2026] 127.0.0.1:50832 Closing
+[Thu Jul 30 18:41:31 2026] 127.0.0.1:44220 Accepted
+[Thu Jul 30 18:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:32 2026] 127.0.0.1:44220 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:32 2026] 127.0.0.1:44220 Closing
+[Thu Jul 30 18:41:32 2026] 127.0.0.1:44222 Accepted
+[Thu Jul 30 18:41:32 2026] 127.0.0.1:44222 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:32 2026] 127.0.0.1:44222 Closing
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59136 Accepted
+[Thu Jul 30 18:41:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59136 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59136 Closing
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59142 Accepted
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59142 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:41 2026] 127.0.0.1:59142 Closing
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34988 Accepted
+[Thu Jul 30 18:41:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34988 [200]: GET /api/stats.php
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34988 Closing
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34994 Accepted
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34994 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:41:51 2026] 127.0.0.1:34994 Closing
+[Thu Jul 30 18:42:01 2026] 127.0.0.1:56370 Accepted
+[Thu Jul 30 18:42:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:01 2026] 127.0.0.1:56370 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:01 2026] 127.0.0.1:56370 Closing
+[Thu Jul 30 18:42:02 2026] 127.0.0.1:56378 Accepted
+[Thu Jul 30 18:42:02 2026] 127.0.0.1:56378 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:02 2026] 127.0.0.1:56378 Closing
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55796 Accepted
+[Thu Jul 30 18:42:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55796 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55796 Closing
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55812 Accepted
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55812 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:11 2026] 127.0.0.1:55812 Closing
+[Thu Jul 30 18:42:21 2026] 127.0.0.1:47696 Accepted
+[Thu Jul 30 18:42:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:21 2026] 127.0.0.1:47696 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:21 2026] 127.0.0.1:47696 Closing
+[Thu Jul 30 18:42:22 2026] 127.0.0.1:47706 Accepted
+[Thu Jul 30 18:42:22 2026] 127.0.0.1:47706 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:22 2026] 127.0.0.1:47706 Closing
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38428 Accepted
+[Thu Jul 30 18:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38428 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38428 Closing
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38442 Accepted
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38442 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:31 2026] 127.0.0.1:38442 Closing
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33438 Accepted
+[Thu Jul 30 18:42:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33438 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33438 Closing
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33442 Accepted
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33442 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:41 2026] 127.0.0.1:33442 Closing
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42230 Accepted
+[Thu Jul 30 18:42:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42230 [200]: GET /api/stats.php
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42230 Closing
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42246 Accepted
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42246 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:42:51 2026] 127.0.0.1:42246 Closing
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40020 Accepted
+[Thu Jul 30 18:43:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40020 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40020 Closing
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40028 Accepted
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40028 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:01 2026] 127.0.0.1:40028 Closing
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53108 Accepted
+[Thu Jul 30 18:43:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53108 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53108 Closing
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53124 Accepted
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53124 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:11 2026] 127.0.0.1:53124 Closing
+[Thu Jul 30 18:43:21 2026] 127.0.0.1:51652 Accepted
+[Thu Jul 30 18:43:22 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:22 2026] 127.0.0.1:51652 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:22 2026] 127.0.0.1:51652 Closing
+[Thu Jul 30 18:43:22 2026] 127.0.0.1:51662 Accepted
+[Thu Jul 30 18:43:22 2026] 127.0.0.1:51662 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:22 2026] 127.0.0.1:51662 Closing
+[Thu Jul 30 18:43:31 2026] 127.0.0.1:38778 Accepted
+[Thu Jul 30 18:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:32 2026] 127.0.0.1:38778 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:32 2026] 127.0.0.1:38778 Closing
+[Thu Jul 30 18:43:32 2026] 127.0.0.1:38780 Accepted
+[Thu Jul 30 18:43:32 2026] 127.0.0.1:38780 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:32 2026] 127.0.0.1:38780 Closing
+[Thu Jul 30 18:43:41 2026] 127.0.0.1:45972 Accepted
+[Thu Jul 30 18:43:42 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:42 2026] 127.0.0.1:45972 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:42 2026] 127.0.0.1:45972 Closing
+[Thu Jul 30 18:43:42 2026] 127.0.0.1:45982 Accepted
+[Thu Jul 30 18:43:42 2026] 127.0.0.1:45982 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:42 2026] 127.0.0.1:45982 Closing
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56454 Accepted
+[Thu Jul 30 18:43:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56454 [200]: GET /api/stats.php
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56454 Closing
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56468 Accepted
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56468 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:43:51 2026] 127.0.0.1:56468 Closing
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43470 Accepted
+[Thu Jul 30 18:44:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43470 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43470 Closing
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43478 Accepted
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43478 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:01 2026] 127.0.0.1:43478 Closing
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44572 Accepted
+[Thu Jul 30 18:44:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44572 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44572 Closing
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44586 Accepted
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44586 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:11 2026] 127.0.0.1:44586 Closing
+[Thu Jul 30 18:44:21 2026] 127.0.0.1:34204 Accepted
+[Thu Jul 30 18:44:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:22 2026] 127.0.0.1:34204 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:22 2026] 127.0.0.1:34204 Closing
+[Thu Jul 30 18:44:22 2026] 127.0.0.1:34216 Accepted
+[Thu Jul 30 18:44:22 2026] 127.0.0.1:34216 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:22 2026] 127.0.0.1:34216 Closing
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48722 Accepted
+[Thu Jul 30 18:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48722 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48722 Closing
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48726 Accepted
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48726 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:31 2026] 127.0.0.1:48726 Closing
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51434 Accepted
+[Thu Jul 30 18:44:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51434 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51434 Closing
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51440 Accepted
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51440 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:41 2026] 127.0.0.1:51440 Closing
+[Thu Jul 30 18:44:51 2026] 127.0.0.1:37408 Accepted
+[Thu Jul 30 18:44:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:44:51 2026] 127.0.0.1:37408 [200]: GET /api/stats.php
+[Thu Jul 30 18:44:51 2026] 127.0.0.1:37408 Closing
+[Thu Jul 30 18:44:51 2026] 127.0.0.1:37414 Accepted
+[Thu Jul 30 18:44:52 2026] 127.0.0.1:37414 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:44:52 2026] 127.0.0.1:37414 Closing
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40334 Accepted
+[Thu Jul 30 18:45:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40334 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40334 Closing
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40338 Accepted
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40338 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:01 2026] 127.0.0.1:40338 Closing
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49306 Accepted
+[Thu Jul 30 18:45:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49306 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49306 Closing
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49312 Accepted
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49312 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:11 2026] 127.0.0.1:49312 Closing
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36724 Accepted
+[Thu Jul 30 18:45:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36724 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36724 Closing
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36730 Accepted
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36730 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:21 2026] 127.0.0.1:36730 Closing
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43398 Accepted
+[Thu Jul 30 18:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43398 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43398 Closing
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43404 Accepted
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43404 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:31 2026] 127.0.0.1:43404 Closing
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33184 Accepted
+[Thu Jul 30 18:45:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33184 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33184 Closing
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33198 Accepted
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33198 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:41 2026] 127.0.0.1:33198 Closing
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51144 Accepted
+[Thu Jul 30 18:45:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51144 [200]: GET /api/stats.php
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51144 Closing
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51156 Accepted
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51156 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:45:51 2026] 127.0.0.1:51156 Closing
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50016 Accepted
+[Thu Jul 30 18:46:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50016 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50016 Closing
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50026 Accepted
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50026 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:01 2026] 127.0.0.1:50026 Closing
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54588 Accepted
+[Thu Jul 30 18:46:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54588 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54588 Closing
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54598 Accepted
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54598 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:11 2026] 127.0.0.1:54598 Closing
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49346 Accepted
+[Thu Jul 30 18:46:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49346 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49346 Closing
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49352 Accepted
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49352 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:21 2026] 127.0.0.1:49352 Closing
+[Thu Jul 30 18:46:31 2026] 127.0.0.1:58350 Accepted
+[Thu Jul 30 18:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:32 2026] 127.0.0.1:58350 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:32 2026] 127.0.0.1:58350 Closing
+[Thu Jul 30 18:46:32 2026] 127.0.0.1:58364 Accepted
+[Thu Jul 30 18:46:32 2026] 127.0.0.1:58364 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:32 2026] 127.0.0.1:58364 Closing
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52290 Accepted
+[Thu Jul 30 18:46:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52290 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52290 Closing
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52294 Accepted
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52294 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:41 2026] 127.0.0.1:52294 Closing
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58836 Accepted
+[Thu Jul 30 18:46:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58836 [200]: GET /api/stats.php
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58836 Closing
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58840 Accepted
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58840 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:46:51 2026] 127.0.0.1:58840 Closing
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55740 Accepted
+[Thu Jul 30 18:47:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55740 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55740 Closing
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55748 Accepted
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55748 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:01 2026] 127.0.0.1:55748 Closing
+[Thu Jul 30 18:47:11 2026] 127.0.0.1:58080 Accepted
+[Thu Jul 30 18:47:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:11 2026] 127.0.0.1:58080 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:11 2026] 127.0.0.1:58080 Closing
+[Thu Jul 30 18:47:12 2026] 127.0.0.1:58094 Accepted
+[Thu Jul 30 18:47:12 2026] 127.0.0.1:58094 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:12 2026] 127.0.0.1:58094 Closing
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34654 Accepted
+[Thu Jul 30 18:47:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34654 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34654 Closing
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34660 Accepted
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34660 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:21 2026] 127.0.0.1:34660 Closing
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34732 Accepted
+[Thu Jul 30 18:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34732 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34732 Closing
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34742 Accepted
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34742 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:31 2026] 127.0.0.1:34742 Closing
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59658 Accepted
+[Thu Jul 30 18:47:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59658 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59658 Closing
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59672 Accepted
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59672 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:41 2026] 127.0.0.1:59672 Closing
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48122 Accepted
+[Thu Jul 30 18:47:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48122 [200]: GET /api/stats.php
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48122 Closing
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48132 Accepted
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48132 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:47:51 2026] 127.0.0.1:48132 Closing
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38856 Accepted
+[Thu Jul 30 18:48:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38856 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38856 Closing
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38864 Accepted
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38864 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:01 2026] 127.0.0.1:38864 Closing
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55130 Accepted
+[Thu Jul 30 18:48:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55130 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55130 Closing
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55140 Accepted
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55140 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:11 2026] 127.0.0.1:55140 Closing
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41192 Accepted
+[Thu Jul 30 18:48:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41192 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41192 Closing
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41196 Accepted
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41196 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:21 2026] 127.0.0.1:41196 Closing
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60614 Accepted
+[Thu Jul 30 18:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60614 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60614 Closing
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60622 Accepted
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60622 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:31 2026] 127.0.0.1:60622 Closing
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58476 Accepted
+[Thu Jul 30 18:48:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58476 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58476 Closing
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58478 Accepted
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58478 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:41 2026] 127.0.0.1:58478 Closing
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57514 Accepted
+[Thu Jul 30 18:48:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57514 [200]: GET /api/stats.php
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57514 Closing
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57522 Accepted
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57522 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:48:51 2026] 127.0.0.1:57522 Closing
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:58998 Accepted
+[Thu Jul 30 18:49:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:58998 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:58998 Closing
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:59002 Accepted
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:59002 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:01 2026] 127.0.0.1:59002 Closing
+[Thu Jul 30 18:49:11 2026] 127.0.0.1:53050 Accepted
+[Thu Jul 30 18:49:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:12 2026] 127.0.0.1:53050 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:12 2026] 127.0.0.1:53050 Closing
+[Thu Jul 30 18:49:12 2026] 127.0.0.1:53054 Accepted
+[Thu Jul 30 18:49:12 2026] 127.0.0.1:53054 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:12 2026] 127.0.0.1:53054 Closing
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52608 Accepted
+[Thu Jul 30 18:49:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52608 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52608 Closing
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52614 Accepted
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52614 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:21 2026] 127.0.0.1:52614 Closing
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57124 Accepted
+[Thu Jul 30 18:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57124 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57124 Closing
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57132 Accepted
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57132 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:31 2026] 127.0.0.1:57132 Closing
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48588 Accepted
+[Thu Jul 30 18:49:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48588 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48588 Closing
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48600 Accepted
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48600 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:41 2026] 127.0.0.1:48600 Closing
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53150 Accepted
+[Thu Jul 30 18:49:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53150 [200]: GET /api/stats.php
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53150 Closing
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53156 Accepted
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53156 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:49:51 2026] 127.0.0.1:53156 Closing
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39338 Accepted
+[Thu Jul 30 18:50:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39338 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39338 Closing
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39354 Accepted
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39354 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:01 2026] 127.0.0.1:39354 Closing
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55046 Accepted
+[Thu Jul 30 18:50:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55046 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55046 Closing
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55058 Accepted
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55058 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:11 2026] 127.0.0.1:55058 Closing
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56254 Accepted
+[Thu Jul 30 18:50:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56254 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56254 Closing
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56262 Accepted
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56262 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:21 2026] 127.0.0.1:56262 Closing
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49494 Accepted
+[Thu Jul 30 18:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49494 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49494 Closing
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49510 Accepted
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49510 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:31 2026] 127.0.0.1:49510 Closing
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44642 Accepted
+[Thu Jul 30 18:50:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44642 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44642 Closing
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44644 Accepted
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44644 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:41 2026] 127.0.0.1:44644 Closing
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47744 Accepted
+[Thu Jul 30 18:50:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47744 [200]: GET /api/stats.php
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47744 Closing
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47754 Accepted
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47754 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:50:51 2026] 127.0.0.1:47754 Closing
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55230 Accepted
+[Thu Jul 30 18:51:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55230 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55230 Closing
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55246 Accepted
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55246 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:01 2026] 127.0.0.1:55246 Closing
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59182 Accepted
+[Thu Jul 30 18:51:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59182 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59182 Closing
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59192 Accepted
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59192 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:11 2026] 127.0.0.1:59192 Closing
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34526 Accepted
+[Thu Jul 30 18:51:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34526 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34526 Closing
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34542 Accepted
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34542 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:21 2026] 127.0.0.1:34542 Closing
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45464 Accepted
+[Thu Jul 30 18:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45464 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45464 Closing
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45476 Accepted
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45476 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:31 2026] 127.0.0.1:45476 Closing
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46854 Accepted
+[Thu Jul 30 18:51:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46854 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46854 Closing
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46864 Accepted
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46864 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:41 2026] 127.0.0.1:46864 Closing
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45530 Accepted
+[Thu Jul 30 18:51:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45530 [200]: GET /api/stats.php
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45530 Closing
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45546 Accepted
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45546 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:51:51 2026] 127.0.0.1:45546 Closing
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39154 Accepted
+[Thu Jul 30 18:52:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39154 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39154 Closing
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39168 Accepted
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39168 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:01 2026] 127.0.0.1:39168 Closing
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49376 Accepted
+[Thu Jul 30 18:52:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49376 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49376 Closing
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49378 Accepted
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49378 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:11 2026] 127.0.0.1:49378 Closing
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45806 Accepted
+[Thu Jul 30 18:52:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45806 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45806 Closing
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45814 Accepted
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45814 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:21 2026] 127.0.0.1:45814 Closing
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57308 Accepted
+[Thu Jul 30 18:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57308 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57308 Closing
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57320 Accepted
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57320 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:31 2026] 127.0.0.1:57320 Closing
+[Thu Jul 30 18:52:41 2026] 127.0.0.1:48272 Accepted
+[Thu Jul 30 18:52:41 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:42 2026] 127.0.0.1:48272 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:42 2026] 127.0.0.1:48272 Closing
+[Thu Jul 30 18:52:42 2026] 127.0.0.1:48284 Accepted
+[Thu Jul 30 18:52:42 2026] 127.0.0.1:48284 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:42 2026] 127.0.0.1:48284 Closing
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46210 Accepted
+[Thu Jul 30 18:52:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46210 [200]: GET /api/stats.php
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46210 Closing
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46226 Accepted
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46226 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:52:51 2026] 127.0.0.1:46226 Closing
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42748 Accepted
+[Thu Jul 30 18:53:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42748 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42748 Closing
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42752 Accepted
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42752 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:01 2026] 127.0.0.1:42752 Closing
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33678 Accepted
+[Thu Jul 30 18:53:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33678 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33678 Closing
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33684 Accepted
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33684 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:11 2026] 127.0.0.1:33684 Closing
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40868 Accepted
+[Thu Jul 30 18:53:21 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40868 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40868 Closing
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40876 Accepted
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40876 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:21 2026] 127.0.0.1:40876 Closing
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32934 Accepted
+[Thu Jul 30 18:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32934 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32934 Closing
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32940 Accepted
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32940 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:32 2026] 127.0.0.1:32940 Closing
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58008 Accepted
+[Thu Jul 30 18:53:42 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58008 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58008 Closing
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58012 Accepted
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58012 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:42 2026] 127.0.0.1:58012 Closing
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52012 Accepted
+[Thu Jul 30 18:53:52 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52012 [200]: GET /api/stats.php
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52012 Closing
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52016 Accepted
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52016 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:53:52 2026] 127.0.0.1:52016 Closing
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33674 Accepted
+[Thu Jul 30 18:54:02 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33674 [200]: GET /api/stats.php
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33674 Closing
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33688 Accepted
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33688 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:54:02 2026] 127.0.0.1:33688 Closing
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53352 Accepted
+[Thu Jul 30 18:54:12 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53352 [200]: GET /api/stats.php
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53352 Closing
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53360 Accepted
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53360 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:54:12 2026] 127.0.0.1:53360 Closing
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48814 Accepted
+[Thu Jul 30 18:54:22 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48814 [200]: GET /api/stats.php
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48814 Closing
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48820 Accepted
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48820 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:54:22 2026] 127.0.0.1:48820 Closing
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51256 Accepted
+[Thu Jul 30 18:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51256 [200]: GET /api/stats.php
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51256 Closing
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51258 Accepted
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51258 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:54:32 2026] 127.0.0.1:51258 Closing
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:46994 Accepted
+[Thu Jul 30 18:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:46994 [200]: GET /api/stats.php
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:46994 Closing
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:47006 Accepted
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:47006 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:55:32 2026] 127.0.0.1:47006 Closing
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40128 Accepted
+[Thu Jul 30 18:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40128 [200]: GET /api/stats.php
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40128 Closing
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40140 Accepted
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40140 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:56:32 2026] 127.0.0.1:40140 Closing
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34982 Accepted
+[Thu Jul 30 18:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34982 [200]: GET /api/stats.php
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34982 Closing
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34992 Accepted
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34992 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:57:32 2026] 127.0.0.1:34992 Closing
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40340 Accepted
+[Thu Jul 30 18:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40340 [200]: GET /api/stats.php
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40340 Closing
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40356 Accepted
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40356 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:58:32 2026] 127.0.0.1:40356 Closing
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45724 Accepted
+[Thu Jul 30 18:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45724 [200]: GET /api/stats.php
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45724 Closing
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45730 Accepted
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45730 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 18:59:32 2026] 127.0.0.1:45730 Closing
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47778 Accepted
+[Thu Jul 30 19:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47778 [200]: GET /api/stats.php
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47778 Closing
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47788 Accepted
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47788 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:00:32 2026] 127.0.0.1:47788 Closing
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50706 Accepted
+[Thu Jul 30 19:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50706 [200]: GET /api/stats.php
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50706 Closing
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50716 Accepted
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50716 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:01:32 2026] 127.0.0.1:50716 Closing
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48880 Accepted
+[Thu Jul 30 19:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48880 [200]: GET /api/stats.php
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48880 Closing
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48892 Accepted
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48892 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:02:32 2026] 127.0.0.1:48892 Closing
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36442 Accepted
+[Thu Jul 30 19:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36442 [200]: GET /api/stats.php
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36442 Closing
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36450 Accepted
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36450 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:03:32 2026] 127.0.0.1:36450 Closing
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44888 Accepted
+[Thu Jul 30 19:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44888 [200]: GET /api/stats.php
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44888 Closing
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44894 Accepted
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44894 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:04:32 2026] 127.0.0.1:44894 Closing
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56092 Accepted
+[Thu Jul 30 19:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56092 [200]: GET /api/stats.php
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56092 Closing
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56100 Accepted
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56100 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:05:32 2026] 127.0.0.1:56100 Closing
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47870 Accepted
+[Thu Jul 30 19:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47870 [200]: GET /api/stats.php
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47870 Closing
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47886 Accepted
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47886 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:06:32 2026] 127.0.0.1:47886 Closing
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54370 Accepted
+[Thu Jul 30 19:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54370 [200]: GET /api/stats.php
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54370 Closing
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54372 Accepted
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54372 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:07:32 2026] 127.0.0.1:54372 Closing
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46176 Accepted
+[Thu Jul 30 19:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46176 [200]: GET /api/stats.php
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46176 Closing
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46180 Accepted
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46180 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:08:32 2026] 127.0.0.1:46180 Closing
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39224 Accepted
+[Thu Jul 30 19:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39224 [200]: GET /api/stats.php
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39224 Closing
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39226 Accepted
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39226 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:09:32 2026] 127.0.0.1:39226 Closing
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54870 Accepted
+[Thu Jul 30 19:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54870 [200]: GET /api/stats.php
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54870 Closing
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54886 Accepted
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54886 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:10:32 2026] 127.0.0.1:54886 Closing
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43926 Accepted
+[Thu Jul 30 19:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43926 [200]: GET /api/stats.php
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43926 Closing
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43940 Accepted
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43940 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:11:32 2026] 127.0.0.1:43940 Closing
+[Thu Jul 30 19:11:41 2026] 127.0.0.1:46102 Accepted
+[Thu Jul 30 19:11:42 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:11:42 2026] 127.0.0.1:46102 [200]: GET /api/stats.php
+[Thu Jul 30 19:11:42 2026] 127.0.0.1:46102 Closing
+[Thu Jul 30 19:11:42 2026] 127.0.0.1:46118 Accepted
+[Thu Jul 30 19:11:42 2026] 127.0.0.1:46118 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:11:42 2026] 127.0.0.1:46118 Closing
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43370 Accepted
+[Thu Jul 30 19:11:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43370 [200]: GET /api/stats.php
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43370 Closing
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43382 Accepted
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43382 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:11:51 2026] 127.0.0.1:43382 Closing
+[Thu Jul 30 19:11:56 2026] 127.0.0.1:60948 Accepted
+[Thu Jul 30 19:11:56 2026] 127.0.0.1:60948 [200]: GET /api/chart.php?range=day
+[Thu Jul 30 19:11:56 2026] 127.0.0.1:60948 Closing
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60964 Accepted
+[Thu Jul 30 19:12:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60964 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60964 Closing
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60980 Accepted
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60980 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:01 2026] 127.0.0.1:60980 Closing
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42056 Accepted
+[Thu Jul 30 19:12:11 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42056 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42056 Closing
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42072 Accepted
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42072 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:11 2026] 127.0.0.1:42072 Closing
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58178 Accepted
+[Thu Jul 30 19:12:22 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58178 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58178 Closing
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58184 Accepted
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58184 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:22 2026] 127.0.0.1:58184 Closing
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55904 Accepted
+[Thu Jul 30 19:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55904 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55904 Closing
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55914 Accepted
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55914 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:32 2026] 127.0.0.1:55914 Closing
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44092 Accepted
+[Thu Jul 30 19:12:42 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44092 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44092 Closing
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44106 Accepted
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44106 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:42 2026] 127.0.0.1:44106 Closing
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58546 Accepted
+[Thu Jul 30 19:12:52 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58546 [200]: GET /api/stats.php
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58546 Closing
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58558 Accepted
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58558 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:12:52 2026] 127.0.0.1:58558 Closing
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49272 Accepted
+[Thu Jul 30 19:13:02 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49272 [200]: GET /api/stats.php
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49272 Closing
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49280 Accepted
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49280 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:13:02 2026] 127.0.0.1:49280 Closing
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35098 Accepted
+[Thu Jul 30 19:13:12 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35098 [200]: GET /api/stats.php
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35098 Closing
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35110 Accepted
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35110 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:13:12 2026] 127.0.0.1:35110 Closing
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59598 Accepted
+[Thu Jul 30 19:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59598 [200]: GET /api/stats.php
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59598 Closing
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59600 Accepted
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59600 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:13:32 2026] 127.0.0.1:59600 Closing
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45782 Accepted
+[Thu Jul 30 19:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45782 [200]: GET /api/stats.php
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45782 Closing
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45796 Accepted
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45796 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:14:32 2026] 127.0.0.1:45796 Closing
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44790 Accepted
+[Thu Jul 30 19:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44790 [200]: GET /api/stats.php
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44790 Closing
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44796 Accepted
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44796 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:15:32 2026] 127.0.0.1:44796 Closing
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43548 Accepted
+[Thu Jul 30 19:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43548 [200]: GET /api/stats.php
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43548 Closing
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43562 Accepted
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43562 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:16:32 2026] 127.0.0.1:43562 Closing
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42794 Accepted
+[Thu Jul 30 19:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42794 [200]: GET /api/stats.php
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42794 Closing
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42808 Accepted
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42808 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:17:32 2026] 127.0.0.1:42808 Closing
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52414 Accepted
+[Thu Jul 30 19:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52414 [200]: GET /api/stats.php
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52414 Closing
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52418 Accepted
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52418 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:18:32 2026] 127.0.0.1:52418 Closing
+[Thu Jul 30 19:19:32 2026] 127.0.0.1:40510 Accepted
+[Thu Jul 30 19:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:19:32 2026] 127.0.0.1:40510 [200]: GET /api/stats.php
+[Thu Jul 30 19:19:32 2026] 127.0.0.1:40510 Closing
+[Thu Jul 30 19:19:33 2026] 127.0.0.1:40516 Accepted
+[Thu Jul 30 19:19:33 2026] 127.0.0.1:40516 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:19:33 2026] 127.0.0.1:40516 Closing
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48384 Accepted
+[Thu Jul 30 19:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48384 [200]: GET /api/stats.php
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48384 Closing
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48400 Accepted
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48400 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:20:32 2026] 127.0.0.1:48400 Closing
+[Thu Jul 30 19:21:31 2026] 127.0.0.1:40390 Accepted
+[Thu Jul 30 19:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:21:32 2026] 127.0.0.1:40390 [200]: GET /api/stats.php
+[Thu Jul 30 19:21:32 2026] 127.0.0.1:40390 Closing
+[Thu Jul 30 19:21:32 2026] 127.0.0.1:40394 Accepted
+[Thu Jul 30 19:21:32 2026] 127.0.0.1:40394 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:21:32 2026] 127.0.0.1:40394 Closing
+[Thu Jul 30 19:22:31 2026] 127.0.0.1:42774 Accepted
+[Thu Jul 30 19:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:22:32 2026] 127.0.0.1:42774 [200]: GET /api/stats.php
+[Thu Jul 30 19:22:32 2026] 127.0.0.1:42774 Closing
+[Thu Jul 30 19:22:32 2026] 127.0.0.1:42784 Accepted
+[Thu Jul 30 19:22:32 2026] 127.0.0.1:42784 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:22:32 2026] 127.0.0.1:42784 Closing
+[Thu Jul 30 19:23:31 2026] 127.0.0.1:58852 Accepted
+[Thu Jul 30 19:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:23:32 2026] 127.0.0.1:58852 [200]: GET /api/stats.php
+[Thu Jul 30 19:23:32 2026] 127.0.0.1:58852 Closing
+[Thu Jul 30 19:23:32 2026] 127.0.0.1:58860 Accepted
+[Thu Jul 30 19:23:32 2026] 127.0.0.1:58860 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:23:32 2026] 127.0.0.1:58860 Closing
+[Thu Jul 30 19:24:31 2026] 127.0.0.1:42238 Accepted
+[Thu Jul 30 19:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:24:32 2026] 127.0.0.1:42238 [200]: GET /api/stats.php
+[Thu Jul 30 19:24:32 2026] 127.0.0.1:42238 Closing
+[Thu Jul 30 19:24:32 2026] 127.0.0.1:42250 Accepted
+[Thu Jul 30 19:24:32 2026] 127.0.0.1:42250 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:24:32 2026] 127.0.0.1:42250 Closing
+[Thu Jul 30 19:25:31 2026] 127.0.0.1:59956 Accepted
+[Thu Jul 30 19:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:25:32 2026] 127.0.0.1:59956 [200]: GET /api/stats.php
+[Thu Jul 30 19:25:32 2026] 127.0.0.1:59956 Closing
+[Thu Jul 30 19:25:32 2026] 127.0.0.1:59968 Accepted
+[Thu Jul 30 19:25:32 2026] 127.0.0.1:59968 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:25:32 2026] 127.0.0.1:59968 Closing
+[Thu Jul 30 19:26:31 2026] 127.0.0.1:56084 Accepted
+[Thu Jul 30 19:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:26:32 2026] 127.0.0.1:56084 [200]: GET /api/stats.php
+[Thu Jul 30 19:26:32 2026] 127.0.0.1:56084 Closing
+[Thu Jul 30 19:26:32 2026] 127.0.0.1:56098 Accepted
+[Thu Jul 30 19:26:32 2026] 127.0.0.1:56098 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:26:32 2026] 127.0.0.1:56098 Closing
+[Thu Jul 30 19:27:31 2026] 127.0.0.1:37308 Accepted
+[Thu Jul 30 19:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:27:32 2026] 127.0.0.1:37308 [200]: GET /api/stats.php
+[Thu Jul 30 19:27:32 2026] 127.0.0.1:37308 Closing
+[Thu Jul 30 19:27:32 2026] 127.0.0.1:37310 Accepted
+[Thu Jul 30 19:27:32 2026] 127.0.0.1:37310 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:27:32 2026] 127.0.0.1:37310 Closing
+[Thu Jul 30 19:28:31 2026] 127.0.0.1:47300 Accepted
+[Thu Jul 30 19:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:28:32 2026] 127.0.0.1:47300 [200]: GET /api/stats.php
+[Thu Jul 30 19:28:32 2026] 127.0.0.1:47300 Closing
+[Thu Jul 30 19:28:32 2026] 127.0.0.1:47316 Accepted
+[Thu Jul 30 19:28:32 2026] 127.0.0.1:47316 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:28:32 2026] 127.0.0.1:47316 Closing
+[Thu Jul 30 19:29:31 2026] 127.0.0.1:39866 Accepted
+[Thu Jul 30 19:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:29:32 2026] 127.0.0.1:39866 [200]: GET /api/stats.php
+[Thu Jul 30 19:29:32 2026] 127.0.0.1:39866 Closing
+[Thu Jul 30 19:29:32 2026] 127.0.0.1:39872 Accepted
+[Thu Jul 30 19:29:32 2026] 127.0.0.1:39872 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:29:32 2026] 127.0.0.1:39872 Closing
+[Thu Jul 30 19:30:31 2026] 127.0.0.1:50920 Accepted
+[Thu Jul 30 19:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:30:32 2026] 127.0.0.1:50920 [200]: GET /api/stats.php
+[Thu Jul 30 19:30:32 2026] 127.0.0.1:50920 Closing
+[Thu Jul 30 19:30:32 2026] 127.0.0.1:50922 Accepted
+[Thu Jul 30 19:30:32 2026] 127.0.0.1:50922 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:30:32 2026] 127.0.0.1:50922 Closing
+[Thu Jul 30 19:31:31 2026] 127.0.0.1:47956 Accepted
+[Thu Jul 30 19:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:31:32 2026] 127.0.0.1:47956 [200]: GET /api/stats.php
+[Thu Jul 30 19:31:32 2026] 127.0.0.1:47956 Closing
+[Thu Jul 30 19:31:32 2026] 127.0.0.1:47966 Accepted
+[Thu Jul 30 19:31:32 2026] 127.0.0.1:47966 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:31:32 2026] 127.0.0.1:47966 Closing
+[Thu Jul 30 19:32:31 2026] 127.0.0.1:58468 Accepted
+[Thu Jul 30 19:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:32:32 2026] 127.0.0.1:58468 [200]: GET /api/stats.php
+[Thu Jul 30 19:32:32 2026] 127.0.0.1:58468 Closing
+[Thu Jul 30 19:32:32 2026] 127.0.0.1:58474 Accepted
+[Thu Jul 30 19:32:32 2026] 127.0.0.1:58474 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:32:32 2026] 127.0.0.1:58474 Closing
+[Thu Jul 30 19:33:31 2026] 127.0.0.1:52032 Accepted
+[Thu Jul 30 19:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:33:32 2026] 127.0.0.1:52032 [200]: GET /api/stats.php
+[Thu Jul 30 19:33:32 2026] 127.0.0.1:52032 Closing
+[Thu Jul 30 19:33:32 2026] 127.0.0.1:52040 Accepted
+[Thu Jul 30 19:33:32 2026] 127.0.0.1:52040 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:33:32 2026] 127.0.0.1:52040 Closing
+[Thu Jul 30 19:34:31 2026] 127.0.0.1:50232 Accepted
+[Thu Jul 30 19:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:34:32 2026] 127.0.0.1:50232 [200]: GET /api/stats.php
+[Thu Jul 30 19:34:32 2026] 127.0.0.1:50232 Closing
+[Thu Jul 30 19:34:32 2026] 127.0.0.1:50234 Accepted
+[Thu Jul 30 19:34:32 2026] 127.0.0.1:50234 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:34:32 2026] 127.0.0.1:50234 Closing
+[Thu Jul 30 19:35:31 2026] 127.0.0.1:47148 Accepted
+[Thu Jul 30 19:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:35:32 2026] 127.0.0.1:47148 [200]: GET /api/stats.php
+[Thu Jul 30 19:35:32 2026] 127.0.0.1:47148 Closing
+[Thu Jul 30 19:35:32 2026] 127.0.0.1:47156 Accepted
+[Thu Jul 30 19:35:32 2026] 127.0.0.1:47156 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:35:32 2026] 127.0.0.1:47156 Closing
+[Thu Jul 30 19:36:31 2026] 127.0.0.1:51068 Accepted
+[Thu Jul 30 19:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:36:32 2026] 127.0.0.1:51068 [200]: GET /api/stats.php
+[Thu Jul 30 19:36:32 2026] 127.0.0.1:51068 Closing
+[Thu Jul 30 19:36:32 2026] 127.0.0.1:51076 Accepted
+[Thu Jul 30 19:36:32 2026] 127.0.0.1:51076 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:36:32 2026] 127.0.0.1:51076 Closing
+[Thu Jul 30 19:37:31 2026] 127.0.0.1:53708 Accepted
+[Thu Jul 30 19:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:37:32 2026] 127.0.0.1:53708 [200]: GET /api/stats.php
+[Thu Jul 30 19:37:32 2026] 127.0.0.1:53708 Closing
+[Thu Jul 30 19:37:32 2026] 127.0.0.1:53720 Accepted
+[Thu Jul 30 19:37:32 2026] 127.0.0.1:53720 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:37:32 2026] 127.0.0.1:53720 Closing
+[Thu Jul 30 19:38:31 2026] 127.0.0.1:51148 Accepted
+[Thu Jul 30 19:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:38:32 2026] 127.0.0.1:51148 [200]: GET /api/stats.php
+[Thu Jul 30 19:38:32 2026] 127.0.0.1:51148 Closing
+[Thu Jul 30 19:38:32 2026] 127.0.0.1:51162 Accepted
+[Thu Jul 30 19:38:32 2026] 127.0.0.1:51162 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:38:32 2026] 127.0.0.1:51162 Closing
+[Thu Jul 30 19:39:31 2026] 127.0.0.1:34756 Accepted
+[Thu Jul 30 19:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:39:32 2026] 127.0.0.1:34756 [200]: GET /api/stats.php
+[Thu Jul 30 19:39:32 2026] 127.0.0.1:34756 Closing
+[Thu Jul 30 19:39:32 2026] 127.0.0.1:34764 Accepted
+[Thu Jul 30 19:39:32 2026] 127.0.0.1:34764 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:39:32 2026] 127.0.0.1:34764 Closing
+[Thu Jul 30 19:40:31 2026] 127.0.0.1:57396 Accepted
+[Thu Jul 30 19:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:40:32 2026] 127.0.0.1:57396 [200]: GET /api/stats.php
+[Thu Jul 30 19:40:32 2026] 127.0.0.1:57396 Closing
+[Thu Jul 30 19:40:32 2026] 127.0.0.1:57412 Accepted
+[Thu Jul 30 19:40:32 2026] 127.0.0.1:57412 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:40:32 2026] 127.0.0.1:57412 Closing
+[Thu Jul 30 19:41:31 2026] 127.0.0.1:60176 Accepted
+[Thu Jul 30 19:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:41:32 2026] 127.0.0.1:60176 [200]: GET /api/stats.php
+[Thu Jul 30 19:41:32 2026] 127.0.0.1:60176 Closing
+[Thu Jul 30 19:41:32 2026] 127.0.0.1:60178 Accepted
+[Thu Jul 30 19:41:32 2026] 127.0.0.1:60178 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:41:32 2026] 127.0.0.1:60178 Closing
+[Thu Jul 30 19:42:31 2026] 127.0.0.1:54222 Accepted
+[Thu Jul 30 19:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:42:32 2026] 127.0.0.1:54222 [200]: GET /api/stats.php
+[Thu Jul 30 19:42:32 2026] 127.0.0.1:54222 Closing
+[Thu Jul 30 19:42:32 2026] 127.0.0.1:54238 Accepted
+[Thu Jul 30 19:42:32 2026] 127.0.0.1:54238 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:42:32 2026] 127.0.0.1:54238 Closing
+[Thu Jul 30 19:43:31 2026] 127.0.0.1:46878 Accepted
+[Thu Jul 30 19:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:43:32 2026] 127.0.0.1:46878 [200]: GET /api/stats.php
+[Thu Jul 30 19:43:32 2026] 127.0.0.1:46878 Closing
+[Thu Jul 30 19:43:32 2026] 127.0.0.1:46894 Accepted
+[Thu Jul 30 19:43:32 2026] 127.0.0.1:46894 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:43:32 2026] 127.0.0.1:46894 Closing
+[Thu Jul 30 19:44:31 2026] 127.0.0.1:50112 Accepted
+[Thu Jul 30 19:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:44:32 2026] 127.0.0.1:50112 [200]: GET /api/stats.php
+[Thu Jul 30 19:44:32 2026] 127.0.0.1:50112 Closing
+[Thu Jul 30 19:44:32 2026] 127.0.0.1:50118 Accepted
+[Thu Jul 30 19:44:32 2026] 127.0.0.1:50118 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:44:32 2026] 127.0.0.1:50118 Closing
+[Thu Jul 30 19:45:31 2026] 127.0.0.1:37956 Accepted
+[Thu Jul 30 19:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:45:32 2026] 127.0.0.1:37956 [200]: GET /api/stats.php
+[Thu Jul 30 19:45:32 2026] 127.0.0.1:37956 Closing
+[Thu Jul 30 19:45:32 2026] 127.0.0.1:37966 Accepted
+[Thu Jul 30 19:45:32 2026] 127.0.0.1:37966 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:45:32 2026] 127.0.0.1:37966 Closing
+[Thu Jul 30 19:46:31 2026] 127.0.0.1:57894 Accepted
+[Thu Jul 30 19:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:46:32 2026] 127.0.0.1:57894 [200]: GET /api/stats.php
+[Thu Jul 30 19:46:32 2026] 127.0.0.1:57894 Closing
+[Thu Jul 30 19:46:32 2026] 127.0.0.1:57910 Accepted
+[Thu Jul 30 19:46:32 2026] 127.0.0.1:57910 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:46:32 2026] 127.0.0.1:57910 Closing
+[Thu Jul 30 19:47:31 2026] 127.0.0.1:36238 Accepted
+[Thu Jul 30 19:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:47:32 2026] 127.0.0.1:36238 [200]: GET /api/stats.php
+[Thu Jul 30 19:47:32 2026] 127.0.0.1:36238 Closing
+[Thu Jul 30 19:47:32 2026] 127.0.0.1:36250 Accepted
+[Thu Jul 30 19:47:32 2026] 127.0.0.1:36250 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:47:32 2026] 127.0.0.1:36250 Closing
+[Thu Jul 30 19:48:31 2026] 127.0.0.1:44226 Accepted
+[Thu Jul 30 19:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:48:32 2026] 127.0.0.1:44226 [200]: GET /api/stats.php
+[Thu Jul 30 19:48:32 2026] 127.0.0.1:44226 Closing
+[Thu Jul 30 19:48:32 2026] 127.0.0.1:44234 Accepted
+[Thu Jul 30 19:48:32 2026] 127.0.0.1:44234 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:48:32 2026] 127.0.0.1:44234 Closing
+[Thu Jul 30 19:49:31 2026] 127.0.0.1:57082 Accepted
+[Thu Jul 30 19:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:49:32 2026] 127.0.0.1:57082 [200]: GET /api/stats.php
+[Thu Jul 30 19:49:32 2026] 127.0.0.1:57082 Closing
+[Thu Jul 30 19:49:32 2026] 127.0.0.1:57096 Accepted
+[Thu Jul 30 19:49:32 2026] 127.0.0.1:57096 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:49:32 2026] 127.0.0.1:57096 Closing
+[Thu Jul 30 19:50:31 2026] 127.0.0.1:33728 Accepted
+[Thu Jul 30 19:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:50:32 2026] 127.0.0.1:33728 [200]: GET /api/stats.php
+[Thu Jul 30 19:50:32 2026] 127.0.0.1:33728 Closing
+[Thu Jul 30 19:50:32 2026] 127.0.0.1:33742 Accepted
+[Thu Jul 30 19:50:32 2026] 127.0.0.1:33742 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:50:32 2026] 127.0.0.1:33742 Closing
+[Thu Jul 30 19:51:31 2026] 127.0.0.1:57090 Accepted
+[Thu Jul 30 19:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:51:32 2026] 127.0.0.1:57090 [200]: GET /api/stats.php
+[Thu Jul 30 19:51:32 2026] 127.0.0.1:57090 Closing
+[Thu Jul 30 19:51:32 2026] 127.0.0.1:57096 Accepted
+[Thu Jul 30 19:51:32 2026] 127.0.0.1:57096 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:51:32 2026] 127.0.0.1:57096 Closing
+[Thu Jul 30 19:52:31 2026] 127.0.0.1:55712 Accepted
+[Thu Jul 30 19:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:52:32 2026] 127.0.0.1:55712 [200]: GET /api/stats.php
+[Thu Jul 30 19:52:32 2026] 127.0.0.1:55712 Closing
+[Thu Jul 30 19:52:32 2026] 127.0.0.1:55720 Accepted
+[Thu Jul 30 19:52:32 2026] 127.0.0.1:55720 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:52:32 2026] 127.0.0.1:55720 Closing
+[Thu Jul 30 19:53:31 2026] 127.0.0.1:39344 Accepted
+[Thu Jul 30 19:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:53:32 2026] 127.0.0.1:39344 [200]: GET /api/stats.php
+[Thu Jul 30 19:53:32 2026] 127.0.0.1:39344 Closing
+[Thu Jul 30 19:53:32 2026] 127.0.0.1:39356 Accepted
+[Thu Jul 30 19:53:32 2026] 127.0.0.1:39356 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:53:32 2026] 127.0.0.1:39356 Closing
+[Thu Jul 30 19:54:31 2026] 127.0.0.1:47462 Accepted
+[Thu Jul 30 19:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:54:32 2026] 127.0.0.1:47462 [200]: GET /api/stats.php
+[Thu Jul 30 19:54:32 2026] 127.0.0.1:47462 Closing
+[Thu Jul 30 19:54:32 2026] 127.0.0.1:47478 Accepted
+[Thu Jul 30 19:54:32 2026] 127.0.0.1:47478 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:54:32 2026] 127.0.0.1:47478 Closing
+[Thu Jul 30 19:55:31 2026] 127.0.0.1:36456 Accepted
+[Thu Jul 30 19:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:55:32 2026] 127.0.0.1:36456 [200]: GET /api/stats.php
+[Thu Jul 30 19:55:32 2026] 127.0.0.1:36456 Closing
+[Thu Jul 30 19:55:32 2026] 127.0.0.1:36458 Accepted
+[Thu Jul 30 19:55:32 2026] 127.0.0.1:36458 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:55:32 2026] 127.0.0.1:36458 Closing
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49184 Accepted
+[Thu Jul 30 19:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49184 [200]: GET /api/stats.php
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49184 Closing
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49186 Accepted
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49186 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:56:32 2026] 127.0.0.1:49186 Closing
+[Thu Jul 30 19:57:31 2026] 127.0.0.1:44028 Accepted
+[Thu Jul 30 19:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:57:32 2026] 127.0.0.1:44028 [200]: GET /api/stats.php
+[Thu Jul 30 19:57:32 2026] 127.0.0.1:44028 Closing
+[Thu Jul 30 19:57:32 2026] 127.0.0.1:44030 Accepted
+[Thu Jul 30 19:57:32 2026] 127.0.0.1:44030 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:57:32 2026] 127.0.0.1:44030 Closing
+[Thu Jul 30 19:58:31 2026] 127.0.0.1:47702 Accepted
+[Thu Jul 30 19:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:58:32 2026] 127.0.0.1:47702 [200]: GET /api/stats.php
+[Thu Jul 30 19:58:32 2026] 127.0.0.1:47702 Closing
+[Thu Jul 30 19:58:32 2026] 127.0.0.1:47704 Accepted
+[Thu Jul 30 19:58:32 2026] 127.0.0.1:47704 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:58:32 2026] 127.0.0.1:47704 Closing
+[Thu Jul 30 19:59:31 2026] 127.0.0.1:36288 Accepted
+[Thu Jul 30 19:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 19:59:32 2026] 127.0.0.1:36288 [200]: GET /api/stats.php
+[Thu Jul 30 19:59:32 2026] 127.0.0.1:36288 Closing
+[Thu Jul 30 19:59:32 2026] 127.0.0.1:36302 Accepted
+[Thu Jul 30 19:59:32 2026] 127.0.0.1:36302 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 19:59:32 2026] 127.0.0.1:36302 Closing
+[Thu Jul 30 20:00:31 2026] 127.0.0.1:38312 Accepted
+[Thu Jul 30 20:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:00:32 2026] 127.0.0.1:38312 [200]: GET /api/stats.php
+[Thu Jul 30 20:00:32 2026] 127.0.0.1:38312 Closing
+[Thu Jul 30 20:00:32 2026] 127.0.0.1:38326 Accepted
+[Thu Jul 30 20:00:32 2026] 127.0.0.1:38326 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:00:32 2026] 127.0.0.1:38326 Closing
+[Thu Jul 30 20:01:31 2026] 127.0.0.1:32934 Accepted
+[Thu Jul 30 20:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:01:32 2026] 127.0.0.1:32934 [200]: GET /api/stats.php
+[Thu Jul 30 20:01:32 2026] 127.0.0.1:32934 Closing
+[Thu Jul 30 20:01:32 2026] 127.0.0.1:32940 Accepted
+[Thu Jul 30 20:01:32 2026] 127.0.0.1:32940 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:01:32 2026] 127.0.0.1:32940 Closing
+[Thu Jul 30 20:02:31 2026] 127.0.0.1:42866 Accepted
+[Thu Jul 30 20:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:02:32 2026] 127.0.0.1:42866 [200]: GET /api/stats.php
+[Thu Jul 30 20:02:32 2026] 127.0.0.1:42866 Closing
+[Thu Jul 30 20:02:32 2026] 127.0.0.1:42874 Accepted
+[Thu Jul 30 20:02:32 2026] 127.0.0.1:42874 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:02:32 2026] 127.0.0.1:42874 Closing
+[Thu Jul 30 20:03:31 2026] 127.0.0.1:36892 Accepted
+[Thu Jul 30 20:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:03:32 2026] 127.0.0.1:36892 [200]: GET /api/stats.php
+[Thu Jul 30 20:03:32 2026] 127.0.0.1:36892 Closing
+[Thu Jul 30 20:03:32 2026] 127.0.0.1:36908 Accepted
+[Thu Jul 30 20:03:32 2026] 127.0.0.1:36908 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:03:32 2026] 127.0.0.1:36908 Closing
+[Thu Jul 30 20:04:31 2026] 127.0.0.1:36542 Accepted
+[Thu Jul 30 20:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:04:32 2026] 127.0.0.1:36542 [200]: GET /api/stats.php
+[Thu Jul 30 20:04:32 2026] 127.0.0.1:36542 Closing
+[Thu Jul 30 20:04:32 2026] 127.0.0.1:36546 Accepted
+[Thu Jul 30 20:04:32 2026] 127.0.0.1:36546 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:04:32 2026] 127.0.0.1:36546 Closing
+[Thu Jul 30 20:05:31 2026] 127.0.0.1:39150 Accepted
+[Thu Jul 30 20:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:05:32 2026] 127.0.0.1:39150 [200]: GET /api/stats.php
+[Thu Jul 30 20:05:32 2026] 127.0.0.1:39150 Closing
+[Thu Jul 30 20:05:32 2026] 127.0.0.1:39152 Accepted
+[Thu Jul 30 20:05:32 2026] 127.0.0.1:39152 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:05:32 2026] 127.0.0.1:39152 Closing
+[Thu Jul 30 20:06:31 2026] 127.0.0.1:60044 Accepted
+[Thu Jul 30 20:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:06:32 2026] 127.0.0.1:60044 [200]: GET /api/stats.php
+[Thu Jul 30 20:06:32 2026] 127.0.0.1:60044 Closing
+[Thu Jul 30 20:06:32 2026] 127.0.0.1:60052 Accepted
+[Thu Jul 30 20:06:32 2026] 127.0.0.1:60052 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:06:32 2026] 127.0.0.1:60052 Closing
+[Thu Jul 30 20:07:31 2026] 127.0.0.1:38310 Accepted
+[Thu Jul 30 20:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:07:32 2026] 127.0.0.1:38310 [200]: GET /api/stats.php
+[Thu Jul 30 20:07:32 2026] 127.0.0.1:38310 Closing
+[Thu Jul 30 20:07:32 2026] 127.0.0.1:38318 Accepted
+[Thu Jul 30 20:07:32 2026] 127.0.0.1:38318 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:07:32 2026] 127.0.0.1:38318 Closing
+[Thu Jul 30 20:08:31 2026] 127.0.0.1:55460 Accepted
+[Thu Jul 30 20:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:08:32 2026] 127.0.0.1:55460 [200]: GET /api/stats.php
+[Thu Jul 30 20:08:32 2026] 127.0.0.1:55460 Closing
+[Thu Jul 30 20:08:32 2026] 127.0.0.1:55470 Accepted
+[Thu Jul 30 20:08:32 2026] 127.0.0.1:55470 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:08:32 2026] 127.0.0.1:55470 Closing
+[Thu Jul 30 20:09:31 2026] 127.0.0.1:41028 Accepted
+[Thu Jul 30 20:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:09:32 2026] 127.0.0.1:41028 [200]: GET /api/stats.php
+[Thu Jul 30 20:09:32 2026] 127.0.0.1:41028 Closing
+[Thu Jul 30 20:09:32 2026] 127.0.0.1:41044 Accepted
+[Thu Jul 30 20:09:32 2026] 127.0.0.1:41044 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:09:32 2026] 127.0.0.1:41044 Closing
+[Thu Jul 30 20:10:31 2026] 127.0.0.1:33358 Accepted
+[Thu Jul 30 20:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:10:32 2026] 127.0.0.1:33358 [200]: GET /api/stats.php
+[Thu Jul 30 20:10:32 2026] 127.0.0.1:33358 Closing
+[Thu Jul 30 20:10:32 2026] 127.0.0.1:33374 Accepted
+[Thu Jul 30 20:10:32 2026] 127.0.0.1:33374 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:10:32 2026] 127.0.0.1:33374 Closing
+[Thu Jul 30 20:11:31 2026] 127.0.0.1:59944 Accepted
+[Thu Jul 30 20:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:11:32 2026] 127.0.0.1:59944 [200]: GET /api/stats.php
+[Thu Jul 30 20:11:32 2026] 127.0.0.1:59944 Closing
+[Thu Jul 30 20:11:32 2026] 127.0.0.1:59948 Accepted
+[Thu Jul 30 20:11:32 2026] 127.0.0.1:59948 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:11:32 2026] 127.0.0.1:59948 Closing
+[Thu Jul 30 20:12:31 2026] 127.0.0.1:46876 Accepted
+[Thu Jul 30 20:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:12:32 2026] 127.0.0.1:46876 [200]: GET /api/stats.php
+[Thu Jul 30 20:12:32 2026] 127.0.0.1:46876 Closing
+[Thu Jul 30 20:12:32 2026] 127.0.0.1:46882 Accepted
+[Thu Jul 30 20:12:32 2026] 127.0.0.1:46882 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:12:32 2026] 127.0.0.1:46882 Closing
+[Thu Jul 30 20:13:31 2026] 127.0.0.1:39924 Accepted
+[Thu Jul 30 20:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:13:32 2026] 127.0.0.1:39924 [200]: GET /api/stats.php
+[Thu Jul 30 20:13:32 2026] 127.0.0.1:39924 Closing
+[Thu Jul 30 20:13:32 2026] 127.0.0.1:39940 Accepted
+[Thu Jul 30 20:13:32 2026] 127.0.0.1:39940 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:13:32 2026] 127.0.0.1:39940 Closing
+[Thu Jul 30 20:14:31 2026] 127.0.0.1:60320 Accepted
+[Thu Jul 30 20:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:14:32 2026] 127.0.0.1:60320 [200]: GET /api/stats.php
+[Thu Jul 30 20:14:32 2026] 127.0.0.1:60320 Closing
+[Thu Jul 30 20:14:32 2026] 127.0.0.1:60328 Accepted
+[Thu Jul 30 20:14:32 2026] 127.0.0.1:60328 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:14:32 2026] 127.0.0.1:60328 Closing
+[Thu Jul 30 20:15:31 2026] 127.0.0.1:36896 Accepted
+[Thu Jul 30 20:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:15:32 2026] 127.0.0.1:36896 [200]: GET /api/stats.php
+[Thu Jul 30 20:15:32 2026] 127.0.0.1:36896 Closing
+[Thu Jul 30 20:15:32 2026] 127.0.0.1:36906 Accepted
+[Thu Jul 30 20:15:32 2026] 127.0.0.1:36906 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:15:32 2026] 127.0.0.1:36906 Closing
+[Thu Jul 30 20:16:31 2026] 127.0.0.1:47790 Accepted
+[Thu Jul 30 20:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:16:32 2026] 127.0.0.1:47790 [200]: GET /api/stats.php
+[Thu Jul 30 20:16:32 2026] 127.0.0.1:47790 Closing
+[Thu Jul 30 20:16:32 2026] 127.0.0.1:47798 Accepted
+[Thu Jul 30 20:16:32 2026] 127.0.0.1:47798 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:16:32 2026] 127.0.0.1:47798 Closing
+[Thu Jul 30 20:17:31 2026] 127.0.0.1:59534 Accepted
+[Thu Jul 30 20:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:17:32 2026] 127.0.0.1:59534 [200]: GET /api/stats.php
+[Thu Jul 30 20:17:32 2026] 127.0.0.1:59534 Closing
+[Thu Jul 30 20:17:32 2026] 127.0.0.1:59540 Accepted
+[Thu Jul 30 20:17:32 2026] 127.0.0.1:59540 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:17:32 2026] 127.0.0.1:59540 Closing
+[Thu Jul 30 20:18:31 2026] 127.0.0.1:45408 Accepted
+[Thu Jul 30 20:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:18:32 2026] 127.0.0.1:45408 [200]: GET /api/stats.php
+[Thu Jul 30 20:18:32 2026] 127.0.0.1:45408 Closing
+[Thu Jul 30 20:18:32 2026] 127.0.0.1:45412 Accepted
+[Thu Jul 30 20:18:32 2026] 127.0.0.1:45412 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:18:32 2026] 127.0.0.1:45412 Closing
+[Thu Jul 30 20:19:31 2026] 127.0.0.1:41242 Accepted
+[Thu Jul 30 20:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:19:32 2026] 127.0.0.1:41242 [200]: GET /api/stats.php
+[Thu Jul 30 20:19:32 2026] 127.0.0.1:41242 Closing
+[Thu Jul 30 20:19:32 2026] 127.0.0.1:41244 Accepted
+[Thu Jul 30 20:19:32 2026] 127.0.0.1:41244 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:19:32 2026] 127.0.0.1:41244 Closing
+[Thu Jul 30 20:20:31 2026] 127.0.0.1:54402 Accepted
+[Thu Jul 30 20:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:20:32 2026] 127.0.0.1:54402 [200]: GET /api/stats.php
+[Thu Jul 30 20:20:32 2026] 127.0.0.1:54402 Closing
+[Thu Jul 30 20:20:32 2026] 127.0.0.1:54404 Accepted
+[Thu Jul 30 20:20:32 2026] 127.0.0.1:54404 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:20:32 2026] 127.0.0.1:54404 Closing
+[Thu Jul 30 20:21:31 2026] 127.0.0.1:60576 Accepted
+[Thu Jul 30 20:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:21:32 2026] 127.0.0.1:60576 [200]: GET /api/stats.php
+[Thu Jul 30 20:21:32 2026] 127.0.0.1:60576 Closing
+[Thu Jul 30 20:21:32 2026] 127.0.0.1:60584 Accepted
+[Thu Jul 30 20:21:32 2026] 127.0.0.1:60584 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:21:32 2026] 127.0.0.1:60584 Closing
+[Thu Jul 30 20:22:31 2026] 127.0.0.1:57806 Accepted
+[Thu Jul 30 20:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:22:32 2026] 127.0.0.1:57806 [200]: GET /api/stats.php
+[Thu Jul 30 20:22:32 2026] 127.0.0.1:57806 Closing
+[Thu Jul 30 20:22:32 2026] 127.0.0.1:57810 Accepted
+[Thu Jul 30 20:22:32 2026] 127.0.0.1:57810 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:22:32 2026] 127.0.0.1:57810 Closing
+[Thu Jul 30 20:23:31 2026] 127.0.0.1:53718 Accepted
+[Thu Jul 30 20:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:23:32 2026] 127.0.0.1:53718 [200]: GET /api/stats.php
+[Thu Jul 30 20:23:32 2026] 127.0.0.1:53718 Closing
+[Thu Jul 30 20:23:32 2026] 127.0.0.1:53730 Accepted
+[Thu Jul 30 20:23:32 2026] 127.0.0.1:53730 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:23:32 2026] 127.0.0.1:53730 Closing
+[Thu Jul 30 20:24:31 2026] 127.0.0.1:45126 Accepted
+[Thu Jul 30 20:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:24:32 2026] 127.0.0.1:45126 [200]: GET /api/stats.php
+[Thu Jul 30 20:24:32 2026] 127.0.0.1:45126 Closing
+[Thu Jul 30 20:24:32 2026] 127.0.0.1:45132 Accepted
+[Thu Jul 30 20:24:32 2026] 127.0.0.1:45132 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:24:32 2026] 127.0.0.1:45132 Closing
+[Thu Jul 30 20:25:31 2026] 127.0.0.1:42900 Accepted
+[Thu Jul 30 20:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:25:32 2026] 127.0.0.1:42900 [200]: GET /api/stats.php
+[Thu Jul 30 20:25:32 2026] 127.0.0.1:42900 Closing
+[Thu Jul 30 20:25:32 2026] 127.0.0.1:42916 Accepted
+[Thu Jul 30 20:25:32 2026] 127.0.0.1:42916 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:25:32 2026] 127.0.0.1:42916 Closing
+[Thu Jul 30 20:26:31 2026] 127.0.0.1:50992 Accepted
+[Thu Jul 30 20:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:26:32 2026] 127.0.0.1:50992 [200]: GET /api/stats.php
+[Thu Jul 30 20:26:32 2026] 127.0.0.1:50992 Closing
+[Thu Jul 30 20:26:32 2026] 127.0.0.1:51000 Accepted
+[Thu Jul 30 20:26:32 2026] 127.0.0.1:51000 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:26:32 2026] 127.0.0.1:51000 Closing
+[Thu Jul 30 20:27:31 2026] 127.0.0.1:47364 Accepted
+[Thu Jul 30 20:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:27:32 2026] 127.0.0.1:47364 [200]: GET /api/stats.php
+[Thu Jul 30 20:27:32 2026] 127.0.0.1:47364 Closing
+[Thu Jul 30 20:27:32 2026] 127.0.0.1:47380 Accepted
+[Thu Jul 30 20:27:32 2026] 127.0.0.1:47380 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:27:32 2026] 127.0.0.1:47380 Closing
+[Thu Jul 30 20:28:31 2026] 127.0.0.1:51454 Accepted
+[Thu Jul 30 20:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:28:32 2026] 127.0.0.1:51454 [200]: GET /api/stats.php
+[Thu Jul 30 20:28:32 2026] 127.0.0.1:51454 Closing
+[Thu Jul 30 20:28:32 2026] 127.0.0.1:51468 Accepted
+[Thu Jul 30 20:28:32 2026] 127.0.0.1:51468 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:28:32 2026] 127.0.0.1:51468 Closing
+[Thu Jul 30 20:29:31 2026] 127.0.0.1:50668 Accepted
+[Thu Jul 30 20:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:29:32 2026] 127.0.0.1:50668 [200]: GET /api/stats.php
+[Thu Jul 30 20:29:32 2026] 127.0.0.1:50668 Closing
+[Thu Jul 30 20:29:32 2026] 127.0.0.1:50684 Accepted
+[Thu Jul 30 20:29:32 2026] 127.0.0.1:50684 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:29:32 2026] 127.0.0.1:50684 Closing
+[Thu Jul 30 20:30:31 2026] 127.0.0.1:40738 Accepted
+[Thu Jul 30 20:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:30:32 2026] 127.0.0.1:40738 [200]: GET /api/stats.php
+[Thu Jul 30 20:30:32 2026] 127.0.0.1:40738 Closing
+[Thu Jul 30 20:30:32 2026] 127.0.0.1:40744 Accepted
+[Thu Jul 30 20:30:32 2026] 127.0.0.1:40744 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:30:32 2026] 127.0.0.1:40744 Closing
+[Thu Jul 30 20:31:31 2026] 127.0.0.1:37320 Accepted
+[Thu Jul 30 20:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:31:32 2026] 127.0.0.1:37320 [200]: GET /api/stats.php
+[Thu Jul 30 20:31:32 2026] 127.0.0.1:37320 Closing
+[Thu Jul 30 20:31:32 2026] 127.0.0.1:37322 Accepted
+[Thu Jul 30 20:31:32 2026] 127.0.0.1:37322 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:31:32 2026] 127.0.0.1:37322 Closing
+[Thu Jul 30 20:32:31 2026] 127.0.0.1:55892 Accepted
+[Thu Jul 30 20:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:32:32 2026] 127.0.0.1:55892 [200]: GET /api/stats.php
+[Thu Jul 30 20:32:32 2026] 127.0.0.1:55892 Closing
+[Thu Jul 30 20:32:32 2026] 127.0.0.1:55906 Accepted
+[Thu Jul 30 20:32:32 2026] 127.0.0.1:55906 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:32:32 2026] 127.0.0.1:55906 Closing
+[Thu Jul 30 20:33:31 2026] 127.0.0.1:46410 Accepted
+[Thu Jul 30 20:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:33:32 2026] 127.0.0.1:46410 [200]: GET /api/stats.php
+[Thu Jul 30 20:33:32 2026] 127.0.0.1:46410 Closing
+[Thu Jul 30 20:33:32 2026] 127.0.0.1:46418 Accepted
+[Thu Jul 30 20:33:32 2026] 127.0.0.1:46418 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:33:32 2026] 127.0.0.1:46418 Closing
+[Thu Jul 30 20:34:31 2026] 127.0.0.1:60024 Accepted
+[Thu Jul 30 20:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:34:32 2026] 127.0.0.1:60024 [200]: GET /api/stats.php
+[Thu Jul 30 20:34:32 2026] 127.0.0.1:60024 Closing
+[Thu Jul 30 20:34:32 2026] 127.0.0.1:60026 Accepted
+[Thu Jul 30 20:34:32 2026] 127.0.0.1:60026 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:34:32 2026] 127.0.0.1:60026 Closing
+[Thu Jul 30 20:35:31 2026] 127.0.0.1:44794 Accepted
+[Thu Jul 30 20:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:35:32 2026] 127.0.0.1:44794 [200]: GET /api/stats.php
+[Thu Jul 30 20:35:32 2026] 127.0.0.1:44794 Closing
+[Thu Jul 30 20:35:32 2026] 127.0.0.1:44808 Accepted
+[Thu Jul 30 20:35:32 2026] 127.0.0.1:44808 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:35:32 2026] 127.0.0.1:44808 Closing
+[Thu Jul 30 20:36:31 2026] 127.0.0.1:38974 Accepted
+[Thu Jul 30 20:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:36:32 2026] 127.0.0.1:38974 [200]: GET /api/stats.php
+[Thu Jul 30 20:36:32 2026] 127.0.0.1:38974 Closing
+[Thu Jul 30 20:36:32 2026] 127.0.0.1:38976 Accepted
+[Thu Jul 30 20:36:32 2026] 127.0.0.1:38976 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:36:32 2026] 127.0.0.1:38976 Closing
+[Thu Jul 30 20:37:31 2026] 127.0.0.1:34120 Accepted
+[Thu Jul 30 20:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:37:32 2026] 127.0.0.1:34120 [200]: GET /api/stats.php
+[Thu Jul 30 20:37:32 2026] 127.0.0.1:34120 Closing
+[Thu Jul 30 20:37:32 2026] 127.0.0.1:34124 Accepted
+[Thu Jul 30 20:37:32 2026] 127.0.0.1:34124 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:37:32 2026] 127.0.0.1:34124 Closing
+[Thu Jul 30 20:38:31 2026] 127.0.0.1:45286 Accepted
+[Thu Jul 30 20:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:38:32 2026] 127.0.0.1:45286 [200]: GET /api/stats.php
+[Thu Jul 30 20:38:32 2026] 127.0.0.1:45286 Closing
+[Thu Jul 30 20:38:32 2026] 127.0.0.1:45292 Accepted
+[Thu Jul 30 20:38:32 2026] 127.0.0.1:45292 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:38:32 2026] 127.0.0.1:45292 Closing
+[Thu Jul 30 20:39:31 2026] 127.0.0.1:37502 Accepted
+[Thu Jul 30 20:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:39:32 2026] 127.0.0.1:37502 [200]: GET /api/stats.php
+[Thu Jul 30 20:39:32 2026] 127.0.0.1:37502 Closing
+[Thu Jul 30 20:39:32 2026] 127.0.0.1:37508 Accepted
+[Thu Jul 30 20:39:32 2026] 127.0.0.1:37508 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:39:32 2026] 127.0.0.1:37508 Closing
+[Thu Jul 30 20:40:31 2026] 127.0.0.1:56470 Accepted
+[Thu Jul 30 20:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:40:32 2026] 127.0.0.1:56470 [200]: GET /api/stats.php
+[Thu Jul 30 20:40:32 2026] 127.0.0.1:56470 Closing
+[Thu Jul 30 20:40:32 2026] 127.0.0.1:56476 Accepted
+[Thu Jul 30 20:40:32 2026] 127.0.0.1:56476 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:40:32 2026] 127.0.0.1:56476 Closing
+[Thu Jul 30 20:41:31 2026] 127.0.0.1:33616 Accepted
+[Thu Jul 30 20:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:41:32 2026] 127.0.0.1:33616 [200]: GET /api/stats.php
+[Thu Jul 30 20:41:32 2026] 127.0.0.1:33616 Closing
+[Thu Jul 30 20:41:32 2026] 127.0.0.1:33628 Accepted
+[Thu Jul 30 20:41:32 2026] 127.0.0.1:33628 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:41:32 2026] 127.0.0.1:33628 Closing
+[Thu Jul 30 20:42:31 2026] 127.0.0.1:49856 Accepted
+[Thu Jul 30 20:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:42:32 2026] 127.0.0.1:49856 [200]: GET /api/stats.php
+[Thu Jul 30 20:42:32 2026] 127.0.0.1:49856 Closing
+[Thu Jul 30 20:42:32 2026] 127.0.0.1:49858 Accepted
+[Thu Jul 30 20:42:32 2026] 127.0.0.1:49858 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:42:32 2026] 127.0.0.1:49858 Closing
+[Thu Jul 30 20:43:31 2026] 127.0.0.1:33348 Accepted
+[Thu Jul 30 20:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:43:32 2026] 127.0.0.1:33348 [200]: GET /api/stats.php
+[Thu Jul 30 20:43:32 2026] 127.0.0.1:33348 Closing
+[Thu Jul 30 20:43:32 2026] 127.0.0.1:33358 Accepted
+[Thu Jul 30 20:43:32 2026] 127.0.0.1:33358 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:43:32 2026] 127.0.0.1:33358 Closing
+[Thu Jul 30 20:44:31 2026] 127.0.0.1:51672 Accepted
+[Thu Jul 30 20:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:44:32 2026] 127.0.0.1:51672 [200]: GET /api/stats.php
+[Thu Jul 30 20:44:32 2026] 127.0.0.1:51672 Closing
+[Thu Jul 30 20:44:32 2026] 127.0.0.1:51688 Accepted
+[Thu Jul 30 20:44:32 2026] 127.0.0.1:51688 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:44:32 2026] 127.0.0.1:51688 Closing
+[Thu Jul 30 20:45:31 2026] 127.0.0.1:35120 Accepted
+[Thu Jul 30 20:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:45:32 2026] 127.0.0.1:35120 [200]: GET /api/stats.php
+[Thu Jul 30 20:45:32 2026] 127.0.0.1:35120 Closing
+[Thu Jul 30 20:45:32 2026] 127.0.0.1:35136 Accepted
+[Thu Jul 30 20:45:32 2026] 127.0.0.1:35136 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:45:32 2026] 127.0.0.1:35136 Closing
+[Thu Jul 30 20:46:31 2026] 127.0.0.1:36312 Accepted
+[Thu Jul 30 20:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:46:32 2026] 127.0.0.1:36312 [200]: GET /api/stats.php
+[Thu Jul 30 20:46:32 2026] 127.0.0.1:36312 Closing
+[Thu Jul 30 20:46:32 2026] 127.0.0.1:36328 Accepted
+[Thu Jul 30 20:46:32 2026] 127.0.0.1:36328 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:46:32 2026] 127.0.0.1:36328 Closing
+[Thu Jul 30 20:47:31 2026] 127.0.0.1:40120 Accepted
+[Thu Jul 30 20:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:47:32 2026] 127.0.0.1:40120 [200]: GET /api/stats.php
+[Thu Jul 30 20:47:32 2026] 127.0.0.1:40120 Closing
+[Thu Jul 30 20:47:32 2026] 127.0.0.1:40122 Accepted
+[Thu Jul 30 20:47:32 2026] 127.0.0.1:40122 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:47:32 2026] 127.0.0.1:40122 Closing
+[Thu Jul 30 20:48:31 2026] 127.0.0.1:56808 Accepted
+[Thu Jul 30 20:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:48:32 2026] 127.0.0.1:56808 [200]: GET /api/stats.php
+[Thu Jul 30 20:48:32 2026] 127.0.0.1:56808 Closing
+[Thu Jul 30 20:48:32 2026] 127.0.0.1:56812 Accepted
+[Thu Jul 30 20:48:32 2026] 127.0.0.1:56812 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:48:32 2026] 127.0.0.1:56812 Closing
+[Thu Jul 30 20:49:31 2026] 127.0.0.1:50466 Accepted
+[Thu Jul 30 20:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:49:32 2026] 127.0.0.1:50466 [200]: GET /api/stats.php
+[Thu Jul 30 20:49:32 2026] 127.0.0.1:50466 Closing
+[Thu Jul 30 20:49:32 2026] 127.0.0.1:50480 Accepted
+[Thu Jul 30 20:49:32 2026] 127.0.0.1:50480 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:49:32 2026] 127.0.0.1:50480 Closing
+[Thu Jul 30 20:50:31 2026] 127.0.0.1:41718 Accepted
+[Thu Jul 30 20:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:50:32 2026] 127.0.0.1:41718 [200]: GET /api/stats.php
+[Thu Jul 30 20:50:32 2026] 127.0.0.1:41718 Closing
+[Thu Jul 30 20:50:32 2026] 127.0.0.1:41724 Accepted
+[Thu Jul 30 20:50:32 2026] 127.0.0.1:41724 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:50:32 2026] 127.0.0.1:41724 Closing
+[Thu Jul 30 20:51:31 2026] 127.0.0.1:33964 Accepted
+[Thu Jul 30 20:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:51:32 2026] 127.0.0.1:33964 [200]: GET /api/stats.php
+[Thu Jul 30 20:51:32 2026] 127.0.0.1:33964 Closing
+[Thu Jul 30 20:51:32 2026] 127.0.0.1:33972 Accepted
+[Thu Jul 30 20:51:32 2026] 127.0.0.1:33972 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:51:32 2026] 127.0.0.1:33972 Closing
+[Thu Jul 30 20:52:31 2026] 127.0.0.1:33552 Accepted
+[Thu Jul 30 20:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:52:32 2026] 127.0.0.1:33552 [200]: GET /api/stats.php
+[Thu Jul 30 20:52:32 2026] 127.0.0.1:33552 Closing
+[Thu Jul 30 20:52:32 2026] 127.0.0.1:33564 Accepted
+[Thu Jul 30 20:52:32 2026] 127.0.0.1:33564 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:52:32 2026] 127.0.0.1:33564 Closing
+[Thu Jul 30 20:53:31 2026] 127.0.0.1:34184 Accepted
+[Thu Jul 30 20:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:53:32 2026] 127.0.0.1:34184 [200]: GET /api/stats.php
+[Thu Jul 30 20:53:32 2026] 127.0.0.1:34184 Closing
+[Thu Jul 30 20:53:32 2026] 127.0.0.1:34200 Accepted
+[Thu Jul 30 20:53:32 2026] 127.0.0.1:34200 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:53:32 2026] 127.0.0.1:34200 Closing
+[Thu Jul 30 20:54:31 2026] 127.0.0.1:45188 Accepted
+[Thu Jul 30 20:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:54:32 2026] 127.0.0.1:45188 [200]: GET /api/stats.php
+[Thu Jul 30 20:54:32 2026] 127.0.0.1:45188 Closing
+[Thu Jul 30 20:54:32 2026] 127.0.0.1:45198 Accepted
+[Thu Jul 30 20:54:32 2026] 127.0.0.1:45198 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:54:32 2026] 127.0.0.1:45198 Closing
+[Thu Jul 30 20:55:31 2026] 127.0.0.1:59456 Accepted
+[Thu Jul 30 20:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:55:32 2026] 127.0.0.1:59456 [200]: GET /api/stats.php
+[Thu Jul 30 20:55:32 2026] 127.0.0.1:59456 Closing
+[Thu Jul 30 20:55:32 2026] 127.0.0.1:59460 Accepted
+[Thu Jul 30 20:55:32 2026] 127.0.0.1:59460 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:55:32 2026] 127.0.0.1:59460 Closing
+[Thu Jul 30 20:56:31 2026] 127.0.0.1:60958 Accepted
+[Thu Jul 30 20:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:56:32 2026] 127.0.0.1:60958 [200]: GET /api/stats.php
+[Thu Jul 30 20:56:32 2026] 127.0.0.1:60958 Closing
+[Thu Jul 30 20:56:32 2026] 127.0.0.1:60972 Accepted
+[Thu Jul 30 20:56:32 2026] 127.0.0.1:60972 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:56:32 2026] 127.0.0.1:60972 Closing
+[Thu Jul 30 20:57:31 2026] 127.0.0.1:37822 Accepted
+[Thu Jul 30 20:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:57:32 2026] 127.0.0.1:37822 [200]: GET /api/stats.php
+[Thu Jul 30 20:57:32 2026] 127.0.0.1:37822 Closing
+[Thu Jul 30 20:57:32 2026] 127.0.0.1:37828 Accepted
+[Thu Jul 30 20:57:32 2026] 127.0.0.1:37828 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:57:32 2026] 127.0.0.1:37828 Closing
+[Thu Jul 30 20:58:31 2026] 127.0.0.1:51612 Accepted
+[Thu Jul 30 20:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:58:32 2026] 127.0.0.1:51612 [200]: GET /api/stats.php
+[Thu Jul 30 20:58:32 2026] 127.0.0.1:51612 Closing
+[Thu Jul 30 20:58:32 2026] 127.0.0.1:51616 Accepted
+[Thu Jul 30 20:58:32 2026] 127.0.0.1:51616 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:58:32 2026] 127.0.0.1:51616 Closing
+[Thu Jul 30 20:59:31 2026] 127.0.0.1:41172 Accepted
+[Thu Jul 30 20:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 20:59:32 2026] 127.0.0.1:41172 [200]: GET /api/stats.php
+[Thu Jul 30 20:59:32 2026] 127.0.0.1:41172 Closing
+[Thu Jul 30 20:59:32 2026] 127.0.0.1:41184 Accepted
+[Thu Jul 30 20:59:32 2026] 127.0.0.1:41184 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 20:59:32 2026] 127.0.0.1:41184 Closing
+[Thu Jul 30 21:00:31 2026] 127.0.0.1:47162 Accepted
+[Thu Jul 30 21:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:00:32 2026] 127.0.0.1:47162 [200]: GET /api/stats.php
+[Thu Jul 30 21:00:32 2026] 127.0.0.1:47162 Closing
+[Thu Jul 30 21:00:32 2026] 127.0.0.1:47164 Accepted
+[Thu Jul 30 21:00:32 2026] 127.0.0.1:47164 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:00:32 2026] 127.0.0.1:47164 Closing
+[Thu Jul 30 21:01:31 2026] 127.0.0.1:40572 Accepted
+[Thu Jul 30 21:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:01:32 2026] 127.0.0.1:40572 [200]: GET /api/stats.php
+[Thu Jul 30 21:01:32 2026] 127.0.0.1:40572 Closing
+[Thu Jul 30 21:01:32 2026] 127.0.0.1:40588 Accepted
+[Thu Jul 30 21:01:32 2026] 127.0.0.1:40588 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:01:32 2026] 127.0.0.1:40588 Closing
+[Thu Jul 30 21:02:31 2026] 127.0.0.1:49644 Accepted
+[Thu Jul 30 21:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:02:32 2026] 127.0.0.1:49644 [200]: GET /api/stats.php
+[Thu Jul 30 21:02:32 2026] 127.0.0.1:49644 Closing
+[Thu Jul 30 21:02:32 2026] 127.0.0.1:49650 Accepted
+[Thu Jul 30 21:02:32 2026] 127.0.0.1:49650 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:02:32 2026] 127.0.0.1:49650 Closing
+[Thu Jul 30 21:03:31 2026] 127.0.0.1:56128 Accepted
+[Thu Jul 30 21:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:03:32 2026] 127.0.0.1:56128 [200]: GET /api/stats.php
+[Thu Jul 30 21:03:32 2026] 127.0.0.1:56128 Closing
+[Thu Jul 30 21:03:32 2026] 127.0.0.1:56142 Accepted
+[Thu Jul 30 21:03:32 2026] 127.0.0.1:56142 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:03:32 2026] 127.0.0.1:56142 Closing
+[Thu Jul 30 21:04:31 2026] 127.0.0.1:35632 Accepted
+[Thu Jul 30 21:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:04:32 2026] 127.0.0.1:35632 [200]: GET /api/stats.php
+[Thu Jul 30 21:04:32 2026] 127.0.0.1:35632 Closing
+[Thu Jul 30 21:04:32 2026] 127.0.0.1:35648 Accepted
+[Thu Jul 30 21:04:32 2026] 127.0.0.1:35648 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:04:32 2026] 127.0.0.1:35648 Closing
+[Thu Jul 30 21:05:31 2026] 127.0.0.1:42278 Accepted
+[Thu Jul 30 21:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:05:32 2026] 127.0.0.1:42278 [200]: GET /api/stats.php
+[Thu Jul 30 21:05:32 2026] 127.0.0.1:42278 Closing
+[Thu Jul 30 21:05:32 2026] 127.0.0.1:42292 Accepted
+[Thu Jul 30 21:05:32 2026] 127.0.0.1:42292 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:05:32 2026] 127.0.0.1:42292 Closing
+[Thu Jul 30 21:06:31 2026] 127.0.0.1:45540 Accepted
+[Thu Jul 30 21:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:06:32 2026] 127.0.0.1:45540 [200]: GET /api/stats.php
+[Thu Jul 30 21:06:32 2026] 127.0.0.1:45540 Closing
+[Thu Jul 30 21:06:32 2026] 127.0.0.1:45544 Accepted
+[Thu Jul 30 21:06:32 2026] 127.0.0.1:45544 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:06:32 2026] 127.0.0.1:45544 Closing
+[Thu Jul 30 21:07:31 2026] 127.0.0.1:59658 Accepted
+[Thu Jul 30 21:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:07:32 2026] 127.0.0.1:59658 [200]: GET /api/stats.php
+[Thu Jul 30 21:07:32 2026] 127.0.0.1:59658 Closing
+[Thu Jul 30 21:07:32 2026] 127.0.0.1:59664 Accepted
+[Thu Jul 30 21:07:32 2026] 127.0.0.1:59664 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:07:32 2026] 127.0.0.1:59664 Closing
+[Thu Jul 30 21:08:31 2026] 127.0.0.1:40722 Accepted
+[Thu Jul 30 21:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:08:32 2026] 127.0.0.1:40722 [200]: GET /api/stats.php
+[Thu Jul 30 21:08:32 2026] 127.0.0.1:40722 Closing
+[Thu Jul 30 21:08:32 2026] 127.0.0.1:40738 Accepted
+[Thu Jul 30 21:08:32 2026] 127.0.0.1:40738 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:08:32 2026] 127.0.0.1:40738 Closing
+[Thu Jul 30 21:09:31 2026] 127.0.0.1:44734 Accepted
+[Thu Jul 30 21:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:09:32 2026] 127.0.0.1:44734 [200]: GET /api/stats.php
+[Thu Jul 30 21:09:32 2026] 127.0.0.1:44734 Closing
+[Thu Jul 30 21:09:32 2026] 127.0.0.1:44744 Accepted
+[Thu Jul 30 21:09:32 2026] 127.0.0.1:44744 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:09:32 2026] 127.0.0.1:44744 Closing
+[Thu Jul 30 21:10:31 2026] 127.0.0.1:52080 Accepted
+[Thu Jul 30 21:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:10:32 2026] 127.0.0.1:52080 [200]: GET /api/stats.php
+[Thu Jul 30 21:10:32 2026] 127.0.0.1:52080 Closing
+[Thu Jul 30 21:10:32 2026] 127.0.0.1:52088 Accepted
+[Thu Jul 30 21:10:32 2026] 127.0.0.1:52088 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:10:32 2026] 127.0.0.1:52088 Closing
+[Thu Jul 30 21:11:31 2026] 127.0.0.1:55278 Accepted
+[Thu Jul 30 21:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:11:32 2026] 127.0.0.1:55278 [200]: GET /api/stats.php
+[Thu Jul 30 21:11:32 2026] 127.0.0.1:55278 Closing
+[Thu Jul 30 21:11:32 2026] 127.0.0.1:55282 Accepted
+[Thu Jul 30 21:11:32 2026] 127.0.0.1:55282 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:11:32 2026] 127.0.0.1:55282 Closing
+[Thu Jul 30 21:12:31 2026] 127.0.0.1:44140 Accepted
+[Thu Jul 30 21:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:12:32 2026] 127.0.0.1:44140 [200]: GET /api/stats.php
+[Thu Jul 30 21:12:32 2026] 127.0.0.1:44140 Closing
+[Thu Jul 30 21:12:32 2026] 127.0.0.1:44150 Accepted
+[Thu Jul 30 21:12:32 2026] 127.0.0.1:44150 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:12:32 2026] 127.0.0.1:44150 Closing
+[Thu Jul 30 21:13:31 2026] 127.0.0.1:54238 Accepted
+[Thu Jul 30 21:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:13:32 2026] 127.0.0.1:54238 [200]: GET /api/stats.php
+[Thu Jul 30 21:13:32 2026] 127.0.0.1:54238 Closing
+[Thu Jul 30 21:13:32 2026] 127.0.0.1:54250 Accepted
+[Thu Jul 30 21:13:32 2026] 127.0.0.1:54250 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:13:32 2026] 127.0.0.1:54250 Closing
+[Thu Jul 30 21:14:31 2026] 127.0.0.1:53708 Accepted
+[Thu Jul 30 21:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:14:32 2026] 127.0.0.1:53708 [200]: GET /api/stats.php
+[Thu Jul 30 21:14:32 2026] 127.0.0.1:53708 Closing
+[Thu Jul 30 21:14:32 2026] 127.0.0.1:53722 Accepted
+[Thu Jul 30 21:14:32 2026] 127.0.0.1:53722 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:14:32 2026] 127.0.0.1:53722 Closing
+[Thu Jul 30 21:15:31 2026] 127.0.0.1:46162 Accepted
+[Thu Jul 30 21:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:15:32 2026] 127.0.0.1:46162 [200]: GET /api/stats.php
+[Thu Jul 30 21:15:32 2026] 127.0.0.1:46162 Closing
+[Thu Jul 30 21:15:32 2026] 127.0.0.1:46164 Accepted
+[Thu Jul 30 21:15:32 2026] 127.0.0.1:46164 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:15:32 2026] 127.0.0.1:46164 Closing
+[Thu Jul 30 21:16:31 2026] 127.0.0.1:47842 Accepted
+[Thu Jul 30 21:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:16:32 2026] 127.0.0.1:47842 [200]: GET /api/stats.php
+[Thu Jul 30 21:16:32 2026] 127.0.0.1:47842 Closing
+[Thu Jul 30 21:16:32 2026] 127.0.0.1:47852 Accepted
+[Thu Jul 30 21:16:32 2026] 127.0.0.1:47852 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:16:32 2026] 127.0.0.1:47852 Closing
+[Thu Jul 30 21:17:31 2026] 127.0.0.1:53534 Accepted
+[Thu Jul 30 21:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:17:32 2026] 127.0.0.1:53534 [200]: GET /api/stats.php
+[Thu Jul 30 21:17:32 2026] 127.0.0.1:53534 Closing
+[Thu Jul 30 21:17:32 2026] 127.0.0.1:53536 Accepted
+[Thu Jul 30 21:17:32 2026] 127.0.0.1:53536 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:17:32 2026] 127.0.0.1:53536 Closing
+[Thu Jul 30 21:18:31 2026] 127.0.0.1:41468 Accepted
+[Thu Jul 30 21:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:18:32 2026] 127.0.0.1:41468 [200]: GET /api/stats.php
+[Thu Jul 30 21:18:32 2026] 127.0.0.1:41468 Closing
+[Thu Jul 30 21:18:32 2026] 127.0.0.1:41470 Accepted
+[Thu Jul 30 21:18:32 2026] 127.0.0.1:41470 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:18:32 2026] 127.0.0.1:41470 Closing
+[Thu Jul 30 21:19:31 2026] 127.0.0.1:58556 Accepted
+[Thu Jul 30 21:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:19:32 2026] 127.0.0.1:58556 [200]: GET /api/stats.php
+[Thu Jul 30 21:19:32 2026] 127.0.0.1:58556 Closing
+[Thu Jul 30 21:19:32 2026] 127.0.0.1:58558 Accepted
+[Thu Jul 30 21:19:32 2026] 127.0.0.1:58558 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:19:32 2026] 127.0.0.1:58558 Closing
+[Thu Jul 30 21:20:31 2026] 127.0.0.1:40350 Accepted
+[Thu Jul 30 21:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:20:32 2026] 127.0.0.1:40350 [200]: GET /api/stats.php
+[Thu Jul 30 21:20:32 2026] 127.0.0.1:40350 Closing
+[Thu Jul 30 21:20:32 2026] 127.0.0.1:40352 Accepted
+[Thu Jul 30 21:20:32 2026] 127.0.0.1:40352 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:20:32 2026] 127.0.0.1:40352 Closing
+[Thu Jul 30 21:21:31 2026] 127.0.0.1:41304 Accepted
+[Thu Jul 30 21:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:21:32 2026] 127.0.0.1:41304 [200]: GET /api/stats.php
+[Thu Jul 30 21:21:32 2026] 127.0.0.1:41304 Closing
+[Thu Jul 30 21:21:32 2026] 127.0.0.1:41310 Accepted
+[Thu Jul 30 21:21:32 2026] 127.0.0.1:41310 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:21:32 2026] 127.0.0.1:41310 Closing
+[Thu Jul 30 21:22:31 2026] 127.0.0.1:58506 Accepted
+[Thu Jul 30 21:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:22:32 2026] 127.0.0.1:58506 [200]: GET /api/stats.php
+[Thu Jul 30 21:22:32 2026] 127.0.0.1:58506 Closing
+[Thu Jul 30 21:22:32 2026] 127.0.0.1:58514 Accepted
+[Thu Jul 30 21:22:32 2026] 127.0.0.1:58514 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:22:32 2026] 127.0.0.1:58514 Closing
+[Thu Jul 30 21:23:31 2026] 127.0.0.1:40166 Accepted
+[Thu Jul 30 21:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:23:32 2026] 127.0.0.1:40166 [200]: GET /api/stats.php
+[Thu Jul 30 21:23:32 2026] 127.0.0.1:40166 Closing
+[Thu Jul 30 21:23:32 2026] 127.0.0.1:40176 Accepted
+[Thu Jul 30 21:23:32 2026] 127.0.0.1:40176 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:23:32 2026] 127.0.0.1:40176 Closing
+[Thu Jul 30 21:24:31 2026] 127.0.0.1:46796 Accepted
+[Thu Jul 30 21:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:24:32 2026] 127.0.0.1:46796 [200]: GET /api/stats.php
+[Thu Jul 30 21:24:32 2026] 127.0.0.1:46796 Closing
+[Thu Jul 30 21:24:32 2026] 127.0.0.1:46804 Accepted
+[Thu Jul 30 21:24:32 2026] 127.0.0.1:46804 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:24:32 2026] 127.0.0.1:46804 Closing
+[Thu Jul 30 21:25:31 2026] 127.0.0.1:40880 Accepted
+[Thu Jul 30 21:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:25:32 2026] 127.0.0.1:40880 [200]: GET /api/stats.php
+[Thu Jul 30 21:25:32 2026] 127.0.0.1:40880 Closing
+[Thu Jul 30 21:25:32 2026] 127.0.0.1:40888 Accepted
+[Thu Jul 30 21:25:32 2026] 127.0.0.1:40888 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:25:32 2026] 127.0.0.1:40888 Closing
+[Thu Jul 30 21:26:31 2026] 127.0.0.1:44550 Accepted
+[Thu Jul 30 21:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:26:32 2026] 127.0.0.1:44550 [200]: GET /api/stats.php
+[Thu Jul 30 21:26:32 2026] 127.0.0.1:44550 Closing
+[Thu Jul 30 21:26:32 2026] 127.0.0.1:44566 Accepted
+[Thu Jul 30 21:26:32 2026] 127.0.0.1:44566 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:26:32 2026] 127.0.0.1:44566 Closing
+[Thu Jul 30 21:27:31 2026] 127.0.0.1:46964 Accepted
+[Thu Jul 30 21:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:27:32 2026] 127.0.0.1:46964 [200]: GET /api/stats.php
+[Thu Jul 30 21:27:32 2026] 127.0.0.1:46964 Closing
+[Thu Jul 30 21:27:32 2026] 127.0.0.1:46976 Accepted
+[Thu Jul 30 21:27:32 2026] 127.0.0.1:46976 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:27:32 2026] 127.0.0.1:46976 Closing
+[Thu Jul 30 21:28:31 2026] 127.0.0.1:52032 Accepted
+[Thu Jul 30 21:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:28:32 2026] 127.0.0.1:52032 [200]: GET /api/stats.php
+[Thu Jul 30 21:28:32 2026] 127.0.0.1:52032 Closing
+[Thu Jul 30 21:28:32 2026] 127.0.0.1:52040 Accepted
+[Thu Jul 30 21:28:32 2026] 127.0.0.1:52040 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:28:32 2026] 127.0.0.1:52040 Closing
+[Thu Jul 30 21:29:31 2026] 127.0.0.1:46192 Accepted
+[Thu Jul 30 21:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:29:32 2026] 127.0.0.1:46192 [200]: GET /api/stats.php
+[Thu Jul 30 21:29:32 2026] 127.0.0.1:46192 Closing
+[Thu Jul 30 21:29:32 2026] 127.0.0.1:46198 Accepted
+[Thu Jul 30 21:29:32 2026] 127.0.0.1:46198 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:29:32 2026] 127.0.0.1:46198 Closing
+[Thu Jul 30 21:30:31 2026] 127.0.0.1:40950 Accepted
+[Thu Jul 30 21:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:30:32 2026] 127.0.0.1:40950 [200]: GET /api/stats.php
+[Thu Jul 30 21:30:32 2026] 127.0.0.1:40950 Closing
+[Thu Jul 30 21:30:32 2026] 127.0.0.1:40962 Accepted
+[Thu Jul 30 21:30:32 2026] 127.0.0.1:40962 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:30:32 2026] 127.0.0.1:40962 Closing
+[Thu Jul 30 21:31:31 2026] 127.0.0.1:55768 Accepted
+[Thu Jul 30 21:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:31:32 2026] 127.0.0.1:55768 [200]: GET /api/stats.php
+[Thu Jul 30 21:31:32 2026] 127.0.0.1:55768 Closing
+[Thu Jul 30 21:31:32 2026] 127.0.0.1:55778 Accepted
+[Thu Jul 30 21:31:32 2026] 127.0.0.1:55778 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:31:32 2026] 127.0.0.1:55778 Closing
+[Thu Jul 30 21:32:31 2026] 127.0.0.1:33226 Accepted
+[Thu Jul 30 21:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:32:32 2026] 127.0.0.1:33226 [200]: GET /api/stats.php
+[Thu Jul 30 21:32:32 2026] 127.0.0.1:33226 Closing
+[Thu Jul 30 21:32:32 2026] 127.0.0.1:33228 Accepted
+[Thu Jul 30 21:32:32 2026] 127.0.0.1:33228 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:32:32 2026] 127.0.0.1:33228 Closing
+[Thu Jul 30 21:33:31 2026] 127.0.0.1:35320 Accepted
+[Thu Jul 30 21:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:33:32 2026] 127.0.0.1:35320 [200]: GET /api/stats.php
+[Thu Jul 30 21:33:32 2026] 127.0.0.1:35320 Closing
+[Thu Jul 30 21:33:32 2026] 127.0.0.1:35328 Accepted
+[Thu Jul 30 21:33:32 2026] 127.0.0.1:35328 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:33:32 2026] 127.0.0.1:35328 Closing
+[Thu Jul 30 21:34:31 2026] 127.0.0.1:60018 Accepted
+[Thu Jul 30 21:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:34:32 2026] 127.0.0.1:60018 [200]: GET /api/stats.php
+[Thu Jul 30 21:34:32 2026] 127.0.0.1:60018 Closing
+[Thu Jul 30 21:34:32 2026] 127.0.0.1:60026 Accepted
+[Thu Jul 30 21:34:32 2026] 127.0.0.1:60026 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:34:32 2026] 127.0.0.1:60026 Closing
+[Thu Jul 30 21:35:31 2026] 127.0.0.1:43094 Accepted
+[Thu Jul 30 21:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:35:32 2026] 127.0.0.1:43094 [200]: GET /api/stats.php
+[Thu Jul 30 21:35:32 2026] 127.0.0.1:43094 Closing
+[Thu Jul 30 21:35:32 2026] 127.0.0.1:43106 Accepted
+[Thu Jul 30 21:35:32 2026] 127.0.0.1:43106 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:35:32 2026] 127.0.0.1:43106 Closing
+[Thu Jul 30 21:36:31 2026] 127.0.0.1:60348 Accepted
+[Thu Jul 30 21:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:36:32 2026] 127.0.0.1:60348 [200]: GET /api/stats.php
+[Thu Jul 30 21:36:32 2026] 127.0.0.1:60348 Closing
+[Thu Jul 30 21:36:32 2026] 127.0.0.1:60352 Accepted
+[Thu Jul 30 21:36:32 2026] 127.0.0.1:60352 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:36:32 2026] 127.0.0.1:60352 Closing
+[Thu Jul 30 21:37:31 2026] 127.0.0.1:54610 Accepted
+[Thu Jul 30 21:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:37:32 2026] 127.0.0.1:54610 [200]: GET /api/stats.php
+[Thu Jul 30 21:37:32 2026] 127.0.0.1:54610 Closing
+[Thu Jul 30 21:37:32 2026] 127.0.0.1:54612 Accepted
+[Thu Jul 30 21:37:32 2026] 127.0.0.1:54612 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:37:32 2026] 127.0.0.1:54612 Closing
+[Thu Jul 30 21:38:31 2026] 127.0.0.1:44304 Accepted
+[Thu Jul 30 21:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:38:32 2026] 127.0.0.1:44304 [200]: GET /api/stats.php
+[Thu Jul 30 21:38:32 2026] 127.0.0.1:44304 Closing
+[Thu Jul 30 21:38:32 2026] 127.0.0.1:44310 Accepted
+[Thu Jul 30 21:38:32 2026] 127.0.0.1:44310 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:38:32 2026] 127.0.0.1:44310 Closing
+[Thu Jul 30 21:39:31 2026] 127.0.0.1:59242 Accepted
+[Thu Jul 30 21:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:39:32 2026] 127.0.0.1:59242 [200]: GET /api/stats.php
+[Thu Jul 30 21:39:32 2026] 127.0.0.1:59242 Closing
+[Thu Jul 30 21:39:32 2026] 127.0.0.1:59248 Accepted
+[Thu Jul 30 21:39:32 2026] 127.0.0.1:59248 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:39:32 2026] 127.0.0.1:59248 Closing
+[Thu Jul 30 21:40:31 2026] 127.0.0.1:33754 Accepted
+[Thu Jul 30 21:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:40:32 2026] 127.0.0.1:33754 [200]: GET /api/stats.php
+[Thu Jul 30 21:40:32 2026] 127.0.0.1:33754 Closing
+[Thu Jul 30 21:40:32 2026] 127.0.0.1:33758 Accepted
+[Thu Jul 30 21:40:32 2026] 127.0.0.1:33758 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:40:32 2026] 127.0.0.1:33758 Closing
+[Thu Jul 30 21:41:31 2026] 127.0.0.1:34706 Accepted
+[Thu Jul 30 21:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:41:32 2026] 127.0.0.1:34706 [200]: GET /api/stats.php
+[Thu Jul 30 21:41:32 2026] 127.0.0.1:34706 Closing
+[Thu Jul 30 21:41:32 2026] 127.0.0.1:34708 Accepted
+[Thu Jul 30 21:41:32 2026] 127.0.0.1:34708 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:41:32 2026] 127.0.0.1:34708 Closing
+[Thu Jul 30 21:42:31 2026] 127.0.0.1:35222 Accepted
+[Thu Jul 30 21:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:42:32 2026] 127.0.0.1:35222 [200]: GET /api/stats.php
+[Thu Jul 30 21:42:32 2026] 127.0.0.1:35222 Closing
+[Thu Jul 30 21:42:32 2026] 127.0.0.1:35228 Accepted
+[Thu Jul 30 21:42:32 2026] 127.0.0.1:35228 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:42:32 2026] 127.0.0.1:35228 Closing
+[Thu Jul 30 21:43:31 2026] 127.0.0.1:43174 Accepted
+[Thu Jul 30 21:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:43:32 2026] 127.0.0.1:43174 [200]: GET /api/stats.php
+[Thu Jul 30 21:43:32 2026] 127.0.0.1:43174 Closing
+[Thu Jul 30 21:43:32 2026] 127.0.0.1:43178 Accepted
+[Thu Jul 30 21:43:32 2026] 127.0.0.1:43178 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:43:32 2026] 127.0.0.1:43178 Closing
+[Thu Jul 30 21:44:31 2026] 127.0.0.1:59450 Accepted
+[Thu Jul 30 21:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:44:32 2026] 127.0.0.1:59450 [200]: GET /api/stats.php
+[Thu Jul 30 21:44:32 2026] 127.0.0.1:59450 Closing
+[Thu Jul 30 21:44:32 2026] 127.0.0.1:59458 Accepted
+[Thu Jul 30 21:44:32 2026] 127.0.0.1:59458 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:44:32 2026] 127.0.0.1:59458 Closing
+[Thu Jul 30 21:45:31 2026] 127.0.0.1:44480 Accepted
+[Thu Jul 30 21:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:45:32 2026] 127.0.0.1:44480 [200]: GET /api/stats.php
+[Thu Jul 30 21:45:32 2026] 127.0.0.1:44480 Closing
+[Thu Jul 30 21:45:32 2026] 127.0.0.1:44484 Accepted
+[Thu Jul 30 21:45:32 2026] 127.0.0.1:44484 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:45:32 2026] 127.0.0.1:44484 Closing
+[Thu Jul 30 21:46:31 2026] 127.0.0.1:60572 Accepted
+[Thu Jul 30 21:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:46:32 2026] 127.0.0.1:60572 [200]: GET /api/stats.php
+[Thu Jul 30 21:46:32 2026] 127.0.0.1:60572 Closing
+[Thu Jul 30 21:46:32 2026] 127.0.0.1:60584 Accepted
+[Thu Jul 30 21:46:32 2026] 127.0.0.1:60584 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:46:32 2026] 127.0.0.1:60584 Closing
+[Thu Jul 30 21:47:31 2026] 127.0.0.1:48916 Accepted
+[Thu Jul 30 21:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:47:32 2026] 127.0.0.1:48916 [200]: GET /api/stats.php
+[Thu Jul 30 21:47:32 2026] 127.0.0.1:48916 Closing
+[Thu Jul 30 21:47:32 2026] 127.0.0.1:48932 Accepted
+[Thu Jul 30 21:47:32 2026] 127.0.0.1:48932 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:47:32 2026] 127.0.0.1:48932 Closing
+[Thu Jul 30 21:48:31 2026] 127.0.0.1:35704 Accepted
+[Thu Jul 30 21:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:48:32 2026] 127.0.0.1:35704 [200]: GET /api/stats.php
+[Thu Jul 30 21:48:32 2026] 127.0.0.1:35704 Closing
+[Thu Jul 30 21:48:32 2026] 127.0.0.1:35706 Accepted
+[Thu Jul 30 21:48:32 2026] 127.0.0.1:35706 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:48:32 2026] 127.0.0.1:35706 Closing
+[Thu Jul 30 21:49:31 2026] 127.0.0.1:60172 Accepted
+[Thu Jul 30 21:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:49:32 2026] 127.0.0.1:60172 [200]: GET /api/stats.php
+[Thu Jul 30 21:49:32 2026] 127.0.0.1:60172 Closing
+[Thu Jul 30 21:49:32 2026] 127.0.0.1:60186 Accepted
+[Thu Jul 30 21:49:32 2026] 127.0.0.1:60186 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:49:32 2026] 127.0.0.1:60186 Closing
+[Thu Jul 30 21:50:31 2026] 127.0.0.1:37590 Accepted
+[Thu Jul 30 21:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:50:32 2026] 127.0.0.1:37590 [200]: GET /api/stats.php
+[Thu Jul 30 21:50:32 2026] 127.0.0.1:37590 Closing
+[Thu Jul 30 21:50:32 2026] 127.0.0.1:37596 Accepted
+[Thu Jul 30 21:50:32 2026] 127.0.0.1:37596 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:50:32 2026] 127.0.0.1:37596 Closing
+[Thu Jul 30 21:51:31 2026] 127.0.0.1:47580 Accepted
+[Thu Jul 30 21:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:51:32 2026] 127.0.0.1:47580 [200]: GET /api/stats.php
+[Thu Jul 30 21:51:32 2026] 127.0.0.1:47580 Closing
+[Thu Jul 30 21:51:32 2026] 127.0.0.1:47592 Accepted
+[Thu Jul 30 21:51:32 2026] 127.0.0.1:47592 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:51:32 2026] 127.0.0.1:47592 Closing
+[Thu Jul 30 21:52:31 2026] 127.0.0.1:51126 Accepted
+[Thu Jul 30 21:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:52:32 2026] 127.0.0.1:51126 [200]: GET /api/stats.php
+[Thu Jul 30 21:52:32 2026] 127.0.0.1:51126 Closing
+[Thu Jul 30 21:52:32 2026] 127.0.0.1:51134 Accepted
+[Thu Jul 30 21:52:32 2026] 127.0.0.1:51134 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:52:32 2026] 127.0.0.1:51134 Closing
+[Thu Jul 30 21:53:31 2026] 127.0.0.1:37338 Accepted
+[Thu Jul 30 21:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:53:32 2026] 127.0.0.1:37338 [200]: GET /api/stats.php
+[Thu Jul 30 21:53:32 2026] 127.0.0.1:37338 Closing
+[Thu Jul 30 21:53:32 2026] 127.0.0.1:37352 Accepted
+[Thu Jul 30 21:53:32 2026] 127.0.0.1:37352 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:53:32 2026] 127.0.0.1:37352 Closing
+[Thu Jul 30 21:54:31 2026] 127.0.0.1:56298 Accepted
+[Thu Jul 30 21:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:54:32 2026] 127.0.0.1:56298 [200]: GET /api/stats.php
+[Thu Jul 30 21:54:32 2026] 127.0.0.1:56298 Closing
+[Thu Jul 30 21:54:32 2026] 127.0.0.1:56310 Accepted
+[Thu Jul 30 21:54:32 2026] 127.0.0.1:56310 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:54:32 2026] 127.0.0.1:56310 Closing
+[Thu Jul 30 21:55:31 2026] 127.0.0.1:49730 Accepted
+[Thu Jul 30 21:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:55:32 2026] 127.0.0.1:49730 [200]: GET /api/stats.php
+[Thu Jul 30 21:55:32 2026] 127.0.0.1:49730 Closing
+[Thu Jul 30 21:55:32 2026] 127.0.0.1:49738 Accepted
+[Thu Jul 30 21:55:32 2026] 127.0.0.1:49738 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:55:32 2026] 127.0.0.1:49738 Closing
+[Thu Jul 30 21:56:31 2026] 127.0.0.1:58420 Accepted
+[Thu Jul 30 21:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:56:32 2026] 127.0.0.1:58420 [200]: GET /api/stats.php
+[Thu Jul 30 21:56:32 2026] 127.0.0.1:58420 Closing
+[Thu Jul 30 21:56:32 2026] 127.0.0.1:58428 Accepted
+[Thu Jul 30 21:56:32 2026] 127.0.0.1:58428 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:56:32 2026] 127.0.0.1:58428 Closing
+[Thu Jul 30 21:57:31 2026] 127.0.0.1:38284 Accepted
+[Thu Jul 30 21:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:57:32 2026] 127.0.0.1:38284 [200]: GET /api/stats.php
+[Thu Jul 30 21:57:32 2026] 127.0.0.1:38284 Closing
+[Thu Jul 30 21:57:32 2026] 127.0.0.1:38298 Accepted
+[Thu Jul 30 21:57:32 2026] 127.0.0.1:38298 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:57:32 2026] 127.0.0.1:38298 Closing
+[Thu Jul 30 21:58:31 2026] 127.0.0.1:52182 Accepted
+[Thu Jul 30 21:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:58:32 2026] 127.0.0.1:52182 [200]: GET /api/stats.php
+[Thu Jul 30 21:58:32 2026] 127.0.0.1:52182 Closing
+[Thu Jul 30 21:58:32 2026] 127.0.0.1:52184 Accepted
+[Thu Jul 30 21:58:32 2026] 127.0.0.1:52184 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:58:32 2026] 127.0.0.1:52184 Closing
+[Thu Jul 30 21:59:31 2026] 127.0.0.1:55432 Accepted
+[Thu Jul 30 21:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 21:59:32 2026] 127.0.0.1:55432 [200]: GET /api/stats.php
+[Thu Jul 30 21:59:32 2026] 127.0.0.1:55432 Closing
+[Thu Jul 30 21:59:32 2026] 127.0.0.1:55440 Accepted
+[Thu Jul 30 21:59:32 2026] 127.0.0.1:55440 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 21:59:32 2026] 127.0.0.1:55440 Closing
+[Thu Jul 30 22:00:31 2026] 127.0.0.1:60158 Accepted
+[Thu Jul 30 22:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:00:32 2026] 127.0.0.1:60158 [200]: GET /api/stats.php
+[Thu Jul 30 22:00:32 2026] 127.0.0.1:60158 Closing
+[Thu Jul 30 22:00:32 2026] 127.0.0.1:60160 Accepted
+[Thu Jul 30 22:00:32 2026] 127.0.0.1:60160 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:00:32 2026] 127.0.0.1:60160 Closing
+[Thu Jul 30 22:01:31 2026] 127.0.0.1:54052 Accepted
+[Thu Jul 30 22:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:01:32 2026] 127.0.0.1:54052 [200]: GET /api/stats.php
+[Thu Jul 30 22:01:32 2026] 127.0.0.1:54052 Closing
+[Thu Jul 30 22:01:32 2026] 127.0.0.1:54062 Accepted
+[Thu Jul 30 22:01:32 2026] 127.0.0.1:54062 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:01:32 2026] 127.0.0.1:54062 Closing
+[Thu Jul 30 22:02:31 2026] 127.0.0.1:36844 Accepted
+[Thu Jul 30 22:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:02:32 2026] 127.0.0.1:36844 [200]: GET /api/stats.php
+[Thu Jul 30 22:02:32 2026] 127.0.0.1:36844 Closing
+[Thu Jul 30 22:02:32 2026] 127.0.0.1:36858 Accepted
+[Thu Jul 30 22:02:32 2026] 127.0.0.1:36858 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:02:32 2026] 127.0.0.1:36858 Closing
+[Thu Jul 30 22:03:31 2026] 127.0.0.1:43484 Accepted
+[Thu Jul 30 22:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:03:32 2026] 127.0.0.1:43484 [200]: GET /api/stats.php
+[Thu Jul 30 22:03:32 2026] 127.0.0.1:43484 Closing
+[Thu Jul 30 22:03:32 2026] 127.0.0.1:43488 Accepted
+[Thu Jul 30 22:03:32 2026] 127.0.0.1:43488 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:03:32 2026] 127.0.0.1:43488 Closing
+[Thu Jul 30 22:04:31 2026] 127.0.0.1:47206 Accepted
+[Thu Jul 30 22:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:04:32 2026] 127.0.0.1:47206 [200]: GET /api/stats.php
+[Thu Jul 30 22:04:32 2026] 127.0.0.1:47206 Closing
+[Thu Jul 30 22:04:32 2026] 127.0.0.1:47210 Accepted
+[Thu Jul 30 22:04:32 2026] 127.0.0.1:47210 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:04:32 2026] 127.0.0.1:47210 Closing
+[Thu Jul 30 22:05:31 2026] 127.0.0.1:40806 Accepted
+[Thu Jul 30 22:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:05:32 2026] 127.0.0.1:40806 [200]: GET /api/stats.php
+[Thu Jul 30 22:05:32 2026] 127.0.0.1:40806 Closing
+[Thu Jul 30 22:05:32 2026] 127.0.0.1:40814 Accepted
+[Thu Jul 30 22:05:32 2026] 127.0.0.1:40814 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:05:32 2026] 127.0.0.1:40814 Closing
+[Thu Jul 30 22:06:31 2026] 127.0.0.1:54234 Accepted
+[Thu Jul 30 22:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:06:32 2026] 127.0.0.1:54234 [200]: GET /api/stats.php
+[Thu Jul 30 22:06:32 2026] 127.0.0.1:54234 Closing
+[Thu Jul 30 22:06:32 2026] 127.0.0.1:54244 Accepted
+[Thu Jul 30 22:06:32 2026] 127.0.0.1:54244 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:06:32 2026] 127.0.0.1:54244 Closing
+[Thu Jul 30 22:07:31 2026] 127.0.0.1:57904 Accepted
+[Thu Jul 30 22:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:07:32 2026] 127.0.0.1:57904 [200]: GET /api/stats.php
+[Thu Jul 30 22:07:32 2026] 127.0.0.1:57904 Closing
+[Thu Jul 30 22:07:32 2026] 127.0.0.1:57920 Accepted
+[Thu Jul 30 22:07:32 2026] 127.0.0.1:57920 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:07:32 2026] 127.0.0.1:57920 Closing
+[Thu Jul 30 22:08:31 2026] 127.0.0.1:53730 Accepted
+[Thu Jul 30 22:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:08:32 2026] 127.0.0.1:53730 [200]: GET /api/stats.php
+[Thu Jul 30 22:08:32 2026] 127.0.0.1:53730 Closing
+[Thu Jul 30 22:08:32 2026] 127.0.0.1:53738 Accepted
+[Thu Jul 30 22:08:32 2026] 127.0.0.1:53738 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:08:32 2026] 127.0.0.1:53738 Closing
+[Thu Jul 30 22:09:31 2026] 127.0.0.1:37764 Accepted
+[Thu Jul 30 22:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:09:32 2026] 127.0.0.1:37764 [200]: GET /api/stats.php
+[Thu Jul 30 22:09:32 2026] 127.0.0.1:37764 Closing
+[Thu Jul 30 22:09:32 2026] 127.0.0.1:37774 Accepted
+[Thu Jul 30 22:09:32 2026] 127.0.0.1:37774 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:09:32 2026] 127.0.0.1:37774 Closing
+[Thu Jul 30 22:10:31 2026] 127.0.0.1:43902 Accepted
+[Thu Jul 30 22:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:10:32 2026] 127.0.0.1:43902 [200]: GET /api/stats.php
+[Thu Jul 30 22:10:32 2026] 127.0.0.1:43902 Closing
+[Thu Jul 30 22:10:32 2026] 127.0.0.1:43904 Accepted
+[Thu Jul 30 22:10:32 2026] 127.0.0.1:43904 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:10:32 2026] 127.0.0.1:43904 Closing
+[Thu Jul 30 22:11:31 2026] 127.0.0.1:42232 Accepted
+[Thu Jul 30 22:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:11:32 2026] 127.0.0.1:42232 [200]: GET /api/stats.php
+[Thu Jul 30 22:11:32 2026] 127.0.0.1:42232 Closing
+[Thu Jul 30 22:11:32 2026] 127.0.0.1:42244 Accepted
+[Thu Jul 30 22:11:32 2026] 127.0.0.1:42244 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:11:32 2026] 127.0.0.1:42244 Closing
+[Thu Jul 30 22:12:31 2026] 127.0.0.1:56308 Accepted
+[Thu Jul 30 22:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:12:32 2026] 127.0.0.1:56308 [200]: GET /api/stats.php
+[Thu Jul 30 22:12:32 2026] 127.0.0.1:56308 Closing
+[Thu Jul 30 22:12:32 2026] 127.0.0.1:56310 Accepted
+[Thu Jul 30 22:12:32 2026] 127.0.0.1:56310 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:12:32 2026] 127.0.0.1:56310 Closing
+[Thu Jul 30 22:13:31 2026] 127.0.0.1:55774 Accepted
+[Thu Jul 30 22:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:13:32 2026] 127.0.0.1:55774 [200]: GET /api/stats.php
+[Thu Jul 30 22:13:32 2026] 127.0.0.1:55774 Closing
+[Thu Jul 30 22:13:32 2026] 127.0.0.1:55776 Accepted
+[Thu Jul 30 22:13:32 2026] 127.0.0.1:55776 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:13:32 2026] 127.0.0.1:55776 Closing
+[Thu Jul 30 22:14:31 2026] 127.0.0.1:55008 Accepted
+[Thu Jul 30 22:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:14:32 2026] 127.0.0.1:55008 [200]: GET /api/stats.php
+[Thu Jul 30 22:14:32 2026] 127.0.0.1:55008 Closing
+[Thu Jul 30 22:14:32 2026] 127.0.0.1:55020 Accepted
+[Thu Jul 30 22:14:32 2026] 127.0.0.1:55020 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:14:32 2026] 127.0.0.1:55020 Closing
+[Thu Jul 30 22:15:31 2026] 127.0.0.1:43078 Accepted
+[Thu Jul 30 22:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:15:32 2026] 127.0.0.1:43078 [200]: GET /api/stats.php
+[Thu Jul 30 22:15:32 2026] 127.0.0.1:43078 Closing
+[Thu Jul 30 22:15:32 2026] 127.0.0.1:43088 Accepted
+[Thu Jul 30 22:15:32 2026] 127.0.0.1:43088 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:15:32 2026] 127.0.0.1:43088 Closing
+[Thu Jul 30 22:16:31 2026] 127.0.0.1:43234 Accepted
+[Thu Jul 30 22:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:16:32 2026] 127.0.0.1:43234 [200]: GET /api/stats.php
+[Thu Jul 30 22:16:32 2026] 127.0.0.1:43234 Closing
+[Thu Jul 30 22:16:32 2026] 127.0.0.1:43238 Accepted
+[Thu Jul 30 22:16:32 2026] 127.0.0.1:43238 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:16:32 2026] 127.0.0.1:43238 Closing
+[Thu Jul 30 22:17:31 2026] 127.0.0.1:49256 Accepted
+[Thu Jul 30 22:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:17:32 2026] 127.0.0.1:49256 [200]: GET /api/stats.php
+[Thu Jul 30 22:17:32 2026] 127.0.0.1:49256 Closing
+[Thu Jul 30 22:17:32 2026] 127.0.0.1:49264 Accepted
+[Thu Jul 30 22:17:32 2026] 127.0.0.1:49264 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:17:32 2026] 127.0.0.1:49264 Closing
+[Thu Jul 30 22:18:31 2026] 127.0.0.1:58172 Accepted
+[Thu Jul 30 22:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:18:32 2026] 127.0.0.1:58172 [200]: GET /api/stats.php
+[Thu Jul 30 22:18:32 2026] 127.0.0.1:58172 Closing
+[Thu Jul 30 22:18:32 2026] 127.0.0.1:58176 Accepted
+[Thu Jul 30 22:18:32 2026] 127.0.0.1:58176 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:18:32 2026] 127.0.0.1:58176 Closing
+[Thu Jul 30 22:19:31 2026] 127.0.0.1:36824 Accepted
+[Thu Jul 30 22:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:19:32 2026] 127.0.0.1:36824 [200]: GET /api/stats.php
+[Thu Jul 30 22:19:32 2026] 127.0.0.1:36824 Closing
+[Thu Jul 30 22:19:32 2026] 127.0.0.1:36836 Accepted
+[Thu Jul 30 22:19:32 2026] 127.0.0.1:36836 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:19:32 2026] 127.0.0.1:36836 Closing
+[Thu Jul 30 22:20:31 2026] 127.0.0.1:48650 Accepted
+[Thu Jul 30 22:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:20:32 2026] 127.0.0.1:48650 [200]: GET /api/stats.php
+[Thu Jul 30 22:20:32 2026] 127.0.0.1:48650 Closing
+[Thu Jul 30 22:20:32 2026] 127.0.0.1:48654 Accepted
+[Thu Jul 30 22:20:32 2026] 127.0.0.1:48654 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:20:32 2026] 127.0.0.1:48654 Closing
+[Thu Jul 30 22:21:31 2026] 127.0.0.1:60088 Accepted
+[Thu Jul 30 22:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:21:32 2026] 127.0.0.1:60088 [200]: GET /api/stats.php
+[Thu Jul 30 22:21:32 2026] 127.0.0.1:60088 Closing
+[Thu Jul 30 22:21:32 2026] 127.0.0.1:60096 Accepted
+[Thu Jul 30 22:21:32 2026] 127.0.0.1:60096 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:21:32 2026] 127.0.0.1:60096 Closing
+[Thu Jul 30 22:22:31 2026] 127.0.0.1:58584 Accepted
+[Thu Jul 30 22:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:22:32 2026] 127.0.0.1:58584 [200]: GET /api/stats.php
+[Thu Jul 30 22:22:32 2026] 127.0.0.1:58584 Closing
+[Thu Jul 30 22:22:32 2026] 127.0.0.1:58588 Accepted
+[Thu Jul 30 22:22:32 2026] 127.0.0.1:58588 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:22:32 2026] 127.0.0.1:58588 Closing
+[Thu Jul 30 22:23:31 2026] 127.0.0.1:41662 Accepted
+[Thu Jul 30 22:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:23:32 2026] 127.0.0.1:41662 [200]: GET /api/stats.php
+[Thu Jul 30 22:23:32 2026] 127.0.0.1:41662 Closing
+[Thu Jul 30 22:23:32 2026] 127.0.0.1:41678 Accepted
+[Thu Jul 30 22:23:32 2026] 127.0.0.1:41678 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:23:32 2026] 127.0.0.1:41678 Closing
+[Thu Jul 30 22:24:31 2026] 127.0.0.1:59782 Accepted
+[Thu Jul 30 22:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:24:32 2026] 127.0.0.1:59782 [200]: GET /api/stats.php
+[Thu Jul 30 22:24:32 2026] 127.0.0.1:59782 Closing
+[Thu Jul 30 22:24:32 2026] 127.0.0.1:59792 Accepted
+[Thu Jul 30 22:24:32 2026] 127.0.0.1:59792 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:24:32 2026] 127.0.0.1:59792 Closing
+[Thu Jul 30 22:25:31 2026] 127.0.0.1:37528 Accepted
+[Thu Jul 30 22:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:25:32 2026] 127.0.0.1:37528 [200]: GET /api/stats.php
+[Thu Jul 30 22:25:32 2026] 127.0.0.1:37528 Closing
+[Thu Jul 30 22:25:32 2026] 127.0.0.1:37540 Accepted
+[Thu Jul 30 22:25:32 2026] 127.0.0.1:37540 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:25:32 2026] 127.0.0.1:37540 Closing
+[Thu Jul 30 22:26:31 2026] 127.0.0.1:45480 Accepted
+[Thu Jul 30 22:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:26:32 2026] 127.0.0.1:45480 [200]: GET /api/stats.php
+[Thu Jul 30 22:26:32 2026] 127.0.0.1:45480 Closing
+[Thu Jul 30 22:26:32 2026] 127.0.0.1:45482 Accepted
+[Thu Jul 30 22:26:32 2026] 127.0.0.1:45482 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:26:32 2026] 127.0.0.1:45482 Closing
+[Thu Jul 30 22:27:31 2026] 127.0.0.1:48730 Accepted
+[Thu Jul 30 22:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:27:32 2026] 127.0.0.1:48730 [200]: GET /api/stats.php
+[Thu Jul 30 22:27:32 2026] 127.0.0.1:48730 Closing
+[Thu Jul 30 22:27:32 2026] 127.0.0.1:48742 Accepted
+[Thu Jul 30 22:27:32 2026] 127.0.0.1:48742 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:27:32 2026] 127.0.0.1:48742 Closing
+[Thu Jul 30 22:28:31 2026] 127.0.0.1:57364 Accepted
+[Thu Jul 30 22:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:28:32 2026] 127.0.0.1:57364 [200]: GET /api/stats.php
+[Thu Jul 30 22:28:32 2026] 127.0.0.1:57364 Closing
+[Thu Jul 30 22:28:32 2026] 127.0.0.1:57378 Accepted
+[Thu Jul 30 22:28:32 2026] 127.0.0.1:57378 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:28:32 2026] 127.0.0.1:57378 Closing
+[Thu Jul 30 22:29:31 2026] 127.0.0.1:41230 Accepted
+[Thu Jul 30 22:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:29:32 2026] 127.0.0.1:41230 [200]: GET /api/stats.php
+[Thu Jul 30 22:29:32 2026] 127.0.0.1:41230 Closing
+[Thu Jul 30 22:29:32 2026] 127.0.0.1:41246 Accepted
+[Thu Jul 30 22:29:32 2026] 127.0.0.1:41246 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:29:32 2026] 127.0.0.1:41246 Closing
+[Thu Jul 30 22:30:31 2026] 127.0.0.1:57878 Accepted
+[Thu Jul 30 22:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:30:32 2026] 127.0.0.1:57878 [200]: GET /api/stats.php
+[Thu Jul 30 22:30:32 2026] 127.0.0.1:57878 Closing
+[Thu Jul 30 22:30:32 2026] 127.0.0.1:57894 Accepted
+[Thu Jul 30 22:30:32 2026] 127.0.0.1:57894 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:30:32 2026] 127.0.0.1:57894 Closing
+[Thu Jul 30 22:31:31 2026] 127.0.0.1:55314 Accepted
+[Thu Jul 30 22:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:31:32 2026] 127.0.0.1:55314 [200]: GET /api/stats.php
+[Thu Jul 30 22:31:32 2026] 127.0.0.1:55314 Closing
+[Thu Jul 30 22:31:32 2026] 127.0.0.1:55318 Accepted
+[Thu Jul 30 22:31:32 2026] 127.0.0.1:55318 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:31:32 2026] 127.0.0.1:55318 Closing
+[Thu Jul 30 22:32:31 2026] 127.0.0.1:39034 Accepted
+[Thu Jul 30 22:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:32:32 2026] 127.0.0.1:39034 [200]: GET /api/stats.php
+[Thu Jul 30 22:32:32 2026] 127.0.0.1:39034 Closing
+[Thu Jul 30 22:32:32 2026] 127.0.0.1:39048 Accepted
+[Thu Jul 30 22:32:32 2026] 127.0.0.1:39048 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:32:32 2026] 127.0.0.1:39048 Closing
+[Thu Jul 30 22:33:31 2026] 127.0.0.1:39936 Accepted
+[Thu Jul 30 22:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:33:32 2026] 127.0.0.1:39936 [200]: GET /api/stats.php
+[Thu Jul 30 22:33:32 2026] 127.0.0.1:39936 Closing
+[Thu Jul 30 22:33:32 2026] 127.0.0.1:39938 Accepted
+[Thu Jul 30 22:33:32 2026] 127.0.0.1:39938 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:33:32 2026] 127.0.0.1:39938 Closing
+[Thu Jul 30 22:34:31 2026] 127.0.0.1:34354 Accepted
+[Thu Jul 30 22:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:34:32 2026] 127.0.0.1:34354 [200]: GET /api/stats.php
+[Thu Jul 30 22:34:32 2026] 127.0.0.1:34354 Closing
+[Thu Jul 30 22:34:32 2026] 127.0.0.1:34370 Accepted
+[Thu Jul 30 22:34:32 2026] 127.0.0.1:34370 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:34:32 2026] 127.0.0.1:34370 Closing
+[Thu Jul 30 22:35:31 2026] 127.0.0.1:34922 Accepted
+[Thu Jul 30 22:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:35:32 2026] 127.0.0.1:34922 [200]: GET /api/stats.php
+[Thu Jul 30 22:35:32 2026] 127.0.0.1:34922 Closing
+[Thu Jul 30 22:35:32 2026] 127.0.0.1:34932 Accepted
+[Thu Jul 30 22:35:32 2026] 127.0.0.1:34932 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:35:32 2026] 127.0.0.1:34932 Closing
+[Thu Jul 30 22:36:31 2026] 127.0.0.1:42066 Accepted
+[Thu Jul 30 22:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:36:32 2026] 127.0.0.1:42066 [200]: GET /api/stats.php
+[Thu Jul 30 22:36:32 2026] 127.0.0.1:42066 Closing
+[Thu Jul 30 22:36:32 2026] 127.0.0.1:42072 Accepted
+[Thu Jul 30 22:36:32 2026] 127.0.0.1:42072 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:36:32 2026] 127.0.0.1:42072 Closing
+[Thu Jul 30 22:37:31 2026] 127.0.0.1:53414 Accepted
+[Thu Jul 30 22:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:37:32 2026] 127.0.0.1:53414 [200]: GET /api/stats.php
+[Thu Jul 30 22:37:32 2026] 127.0.0.1:53414 Closing
+[Thu Jul 30 22:37:32 2026] 127.0.0.1:53418 Accepted
+[Thu Jul 30 22:37:32 2026] 127.0.0.1:53418 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:37:32 2026] 127.0.0.1:53418 Closing
+[Thu Jul 30 22:38:31 2026] 127.0.0.1:41046 Accepted
+[Thu Jul 30 22:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:38:32 2026] 127.0.0.1:41046 [200]: GET /api/stats.php
+[Thu Jul 30 22:38:32 2026] 127.0.0.1:41046 Closing
+[Thu Jul 30 22:38:32 2026] 127.0.0.1:41056 Accepted
+[Thu Jul 30 22:38:32 2026] 127.0.0.1:41056 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:38:32 2026] 127.0.0.1:41056 Closing
+[Thu Jul 30 22:39:31 2026] 127.0.0.1:49096 Accepted
+[Thu Jul 30 22:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:39:32 2026] 127.0.0.1:49096 [200]: GET /api/stats.php
+[Thu Jul 30 22:39:32 2026] 127.0.0.1:49096 Closing
+[Thu Jul 30 22:39:32 2026] 127.0.0.1:49106 Accepted
+[Thu Jul 30 22:39:32 2026] 127.0.0.1:49106 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:39:32 2026] 127.0.0.1:49106 Closing
+[Thu Jul 30 22:40:31 2026] 127.0.0.1:58720 Accepted
+[Thu Jul 30 22:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:40:32 2026] 127.0.0.1:58720 [200]: GET /api/stats.php
+[Thu Jul 30 22:40:32 2026] 127.0.0.1:58720 Closing
+[Thu Jul 30 22:40:32 2026] 127.0.0.1:58734 Accepted
+[Thu Jul 30 22:40:32 2026] 127.0.0.1:58734 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:40:32 2026] 127.0.0.1:58734 Closing
+[Thu Jul 30 22:41:31 2026] 127.0.0.1:43574 Accepted
+[Thu Jul 30 22:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:41:32 2026] 127.0.0.1:43574 [200]: GET /api/stats.php
+[Thu Jul 30 22:41:32 2026] 127.0.0.1:43574 Closing
+[Thu Jul 30 22:41:32 2026] 127.0.0.1:43578 Accepted
+[Thu Jul 30 22:41:32 2026] 127.0.0.1:43578 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:41:32 2026] 127.0.0.1:43578 Closing
+[Thu Jul 30 22:42:31 2026] 127.0.0.1:42076 Accepted
+[Thu Jul 30 22:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:42:32 2026] 127.0.0.1:42076 [200]: GET /api/stats.php
+[Thu Jul 30 22:42:32 2026] 127.0.0.1:42076 Closing
+[Thu Jul 30 22:42:32 2026] 127.0.0.1:42086 Accepted
+[Thu Jul 30 22:42:32 2026] 127.0.0.1:42086 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:42:32 2026] 127.0.0.1:42086 Closing
+[Thu Jul 30 22:43:31 2026] 127.0.0.1:37978 Accepted
+[Thu Jul 30 22:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:43:32 2026] 127.0.0.1:37978 [200]: GET /api/stats.php
+[Thu Jul 30 22:43:32 2026] 127.0.0.1:37978 Closing
+[Thu Jul 30 22:43:32 2026] 127.0.0.1:37994 Accepted
+[Thu Jul 30 22:43:32 2026] 127.0.0.1:37994 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:43:32 2026] 127.0.0.1:37994 Closing
+[Thu Jul 30 22:44:31 2026] 127.0.0.1:56064 Accepted
+[Thu Jul 30 22:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:44:32 2026] 127.0.0.1:56064 [200]: GET /api/stats.php
+[Thu Jul 30 22:44:32 2026] 127.0.0.1:56064 Closing
+[Thu Jul 30 22:44:32 2026] 127.0.0.1:56076 Accepted
+[Thu Jul 30 22:44:32 2026] 127.0.0.1:56076 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:44:32 2026] 127.0.0.1:56076 Closing
+[Thu Jul 30 22:45:31 2026] 127.0.0.1:33268 Accepted
+[Thu Jul 30 22:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:45:32 2026] 127.0.0.1:33268 [200]: GET /api/stats.php
+[Thu Jul 30 22:45:32 2026] 127.0.0.1:33268 Closing
+[Thu Jul 30 22:45:32 2026] 127.0.0.1:33284 Accepted
+[Thu Jul 30 22:45:32 2026] 127.0.0.1:33284 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:45:32 2026] 127.0.0.1:33284 Closing
+[Thu Jul 30 22:46:31 2026] 127.0.0.1:39472 Accepted
+[Thu Jul 30 22:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:46:32 2026] 127.0.0.1:39472 [200]: GET /api/stats.php
+[Thu Jul 30 22:46:32 2026] 127.0.0.1:39472 Closing
+[Thu Jul 30 22:46:32 2026] 127.0.0.1:39480 Accepted
+[Thu Jul 30 22:46:32 2026] 127.0.0.1:39480 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:46:32 2026] 127.0.0.1:39480 Closing
+[Thu Jul 30 22:47:31 2026] 127.0.0.1:54746 Accepted
+[Thu Jul 30 22:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:47:32 2026] 127.0.0.1:54746 [200]: GET /api/stats.php
+[Thu Jul 30 22:47:32 2026] 127.0.0.1:54746 Closing
+[Thu Jul 30 22:47:32 2026] 127.0.0.1:54748 Accepted
+[Thu Jul 30 22:47:32 2026] 127.0.0.1:54748 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:47:32 2026] 127.0.0.1:54748 Closing
+[Thu Jul 30 22:48:31 2026] 127.0.0.1:54134 Accepted
+[Thu Jul 30 22:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:48:32 2026] 127.0.0.1:54134 [200]: GET /api/stats.php
+[Thu Jul 30 22:48:32 2026] 127.0.0.1:54134 Closing
+[Thu Jul 30 22:48:32 2026] 127.0.0.1:54136 Accepted
+[Thu Jul 30 22:48:32 2026] 127.0.0.1:54136 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:48:32 2026] 127.0.0.1:54136 Closing
+[Thu Jul 30 22:49:31 2026] 127.0.0.1:53080 Accepted
+[Thu Jul 30 22:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:49:32 2026] 127.0.0.1:53080 [200]: GET /api/stats.php
+[Thu Jul 30 22:49:32 2026] 127.0.0.1:53080 Closing
+[Thu Jul 30 22:49:32 2026] 127.0.0.1:53084 Accepted
+[Thu Jul 30 22:49:32 2026] 127.0.0.1:53084 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:49:32 2026] 127.0.0.1:53084 Closing
+[Thu Jul 30 22:50:31 2026] 127.0.0.1:40096 Accepted
+[Thu Jul 30 22:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:50:32 2026] 127.0.0.1:40096 [200]: GET /api/stats.php
+[Thu Jul 30 22:50:32 2026] 127.0.0.1:40096 Closing
+[Thu Jul 30 22:50:32 2026] 127.0.0.1:40106 Accepted
+[Thu Jul 30 22:50:32 2026] 127.0.0.1:40106 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:50:32 2026] 127.0.0.1:40106 Closing
+[Thu Jul 30 22:51:31 2026] 127.0.0.1:41442 Accepted
+[Thu Jul 30 22:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:51:32 2026] 127.0.0.1:41442 [200]: GET /api/stats.php
+[Thu Jul 30 22:51:32 2026] 127.0.0.1:41442 Closing
+[Thu Jul 30 22:51:32 2026] 127.0.0.1:41448 Accepted
+[Thu Jul 30 22:51:32 2026] 127.0.0.1:41448 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:51:32 2026] 127.0.0.1:41448 Closing
+[Thu Jul 30 22:52:31 2026] 127.0.0.1:36720 Accepted
+[Thu Jul 30 22:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:52:32 2026] 127.0.0.1:36720 [200]: GET /api/stats.php
+[Thu Jul 30 22:52:32 2026] 127.0.0.1:36720 Closing
+[Thu Jul 30 22:52:32 2026] 127.0.0.1:36732 Accepted
+[Thu Jul 30 22:52:32 2026] 127.0.0.1:36732 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:52:32 2026] 127.0.0.1:36732 Closing
+[Thu Jul 30 22:53:31 2026] 127.0.0.1:46794 Accepted
+[Thu Jul 30 22:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:53:32 2026] 127.0.0.1:46794 [200]: GET /api/stats.php
+[Thu Jul 30 22:53:32 2026] 127.0.0.1:46794 Closing
+[Thu Jul 30 22:53:32 2026] 127.0.0.1:46796 Accepted
+[Thu Jul 30 22:53:32 2026] 127.0.0.1:46796 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:53:32 2026] 127.0.0.1:46796 Closing
+[Thu Jul 30 22:54:31 2026] 127.0.0.1:33064 Accepted
+[Thu Jul 30 22:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:54:32 2026] 127.0.0.1:33064 [200]: GET /api/stats.php
+[Thu Jul 30 22:54:32 2026] 127.0.0.1:33064 Closing
+[Thu Jul 30 22:54:32 2026] 127.0.0.1:33066 Accepted
+[Thu Jul 30 22:54:32 2026] 127.0.0.1:33066 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:54:32 2026] 127.0.0.1:33066 Closing
+[Thu Jul 30 22:55:31 2026] 127.0.0.1:60282 Accepted
+[Thu Jul 30 22:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:55:32 2026] 127.0.0.1:60282 [200]: GET /api/stats.php
+[Thu Jul 30 22:55:32 2026] 127.0.0.1:60282 Closing
+[Thu Jul 30 22:55:32 2026] 127.0.0.1:60298 Accepted
+[Thu Jul 30 22:55:32 2026] 127.0.0.1:60298 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:55:32 2026] 127.0.0.1:60298 Closing
+[Thu Jul 30 22:56:31 2026] 127.0.0.1:33742 Accepted
+[Thu Jul 30 22:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:56:32 2026] 127.0.0.1:33742 [200]: GET /api/stats.php
+[Thu Jul 30 22:56:32 2026] 127.0.0.1:33742 Closing
+[Thu Jul 30 22:56:32 2026] 127.0.0.1:33750 Accepted
+[Thu Jul 30 22:56:32 2026] 127.0.0.1:33750 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:56:32 2026] 127.0.0.1:33750 Closing
+[Thu Jul 30 22:57:31 2026] 127.0.0.1:38680 Accepted
+[Thu Jul 30 22:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:57:32 2026] 127.0.0.1:38680 [200]: GET /api/stats.php
+[Thu Jul 30 22:57:32 2026] 127.0.0.1:38680 Closing
+[Thu Jul 30 22:57:32 2026] 127.0.0.1:38686 Accepted
+[Thu Jul 30 22:57:32 2026] 127.0.0.1:38686 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:57:32 2026] 127.0.0.1:38686 Closing
+[Thu Jul 30 22:58:31 2026] 127.0.0.1:40552 Accepted
+[Thu Jul 30 22:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:58:32 2026] 127.0.0.1:40552 [200]: GET /api/stats.php
+[Thu Jul 30 22:58:32 2026] 127.0.0.1:40552 Closing
+[Thu Jul 30 22:58:32 2026] 127.0.0.1:40564 Accepted
+[Thu Jul 30 22:58:32 2026] 127.0.0.1:40564 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:58:32 2026] 127.0.0.1:40564 Closing
+[Thu Jul 30 22:59:31 2026] 127.0.0.1:33958 Accepted
+[Thu Jul 30 22:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 22:59:32 2026] 127.0.0.1:33958 [200]: GET /api/stats.php
+[Thu Jul 30 22:59:32 2026] 127.0.0.1:33958 Closing
+[Thu Jul 30 22:59:32 2026] 127.0.0.1:33966 Accepted
+[Thu Jul 30 22:59:32 2026] 127.0.0.1:33966 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 22:59:32 2026] 127.0.0.1:33966 Closing
+[Thu Jul 30 23:00:31 2026] 127.0.0.1:47574 Accepted
+[Thu Jul 30 23:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:00:32 2026] 127.0.0.1:47574 [200]: GET /api/stats.php
+[Thu Jul 30 23:00:32 2026] 127.0.0.1:47574 Closing
+[Thu Jul 30 23:00:32 2026] 127.0.0.1:47588 Accepted
+[Thu Jul 30 23:00:32 2026] 127.0.0.1:47588 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:00:32 2026] 127.0.0.1:47588 Closing
+[Thu Jul 30 23:01:31 2026] 127.0.0.1:54516 Accepted
+[Thu Jul 30 23:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:01:32 2026] 127.0.0.1:54516 [200]: GET /api/stats.php
+[Thu Jul 30 23:01:32 2026] 127.0.0.1:54516 Closing
+[Thu Jul 30 23:01:32 2026] 127.0.0.1:54524 Accepted
+[Thu Jul 30 23:01:32 2026] 127.0.0.1:54524 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:01:32 2026] 127.0.0.1:54524 Closing
+[Thu Jul 30 23:02:31 2026] 127.0.0.1:56918 Accepted
+[Thu Jul 30 23:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:02:32 2026] 127.0.0.1:56918 [200]: GET /api/stats.php
+[Thu Jul 30 23:02:32 2026] 127.0.0.1:56918 Closing
+[Thu Jul 30 23:02:32 2026] 127.0.0.1:56920 Accepted
+[Thu Jul 30 23:02:32 2026] 127.0.0.1:56920 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:02:32 2026] 127.0.0.1:56920 Closing
+[Thu Jul 30 23:03:31 2026] 127.0.0.1:40412 Accepted
+[Thu Jul 30 23:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:03:32 2026] 127.0.0.1:40412 [200]: GET /api/stats.php
+[Thu Jul 30 23:03:32 2026] 127.0.0.1:40412 Closing
+[Thu Jul 30 23:03:32 2026] 127.0.0.1:40428 Accepted
+[Thu Jul 30 23:03:32 2026] 127.0.0.1:40428 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:03:32 2026] 127.0.0.1:40428 Closing
+[Thu Jul 30 23:04:31 2026] 127.0.0.1:58112 Accepted
+[Thu Jul 30 23:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:04:32 2026] 127.0.0.1:58112 [200]: GET /api/stats.php
+[Thu Jul 30 23:04:32 2026] 127.0.0.1:58112 Closing
+[Thu Jul 30 23:04:32 2026] 127.0.0.1:58126 Accepted
+[Thu Jul 30 23:04:32 2026] 127.0.0.1:58126 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:04:32 2026] 127.0.0.1:58126 Closing
+[Thu Jul 30 23:05:31 2026] 127.0.0.1:36368 Accepted
+[Thu Jul 30 23:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:05:32 2026] 127.0.0.1:36368 [200]: GET /api/stats.php
+[Thu Jul 30 23:05:32 2026] 127.0.0.1:36368 Closing
+[Thu Jul 30 23:05:32 2026] 127.0.0.1:36374 Accepted
+[Thu Jul 30 23:05:32 2026] 127.0.0.1:36374 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:05:32 2026] 127.0.0.1:36374 Closing
+[Thu Jul 30 23:06:31 2026] 127.0.0.1:39052 Accepted
+[Thu Jul 30 23:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:06:32 2026] 127.0.0.1:39052 [200]: GET /api/stats.php
+[Thu Jul 30 23:06:32 2026] 127.0.0.1:39052 Closing
+[Thu Jul 30 23:06:32 2026] 127.0.0.1:39060 Accepted
+[Thu Jul 30 23:06:32 2026] 127.0.0.1:39060 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:06:32 2026] 127.0.0.1:39060 Closing
+[Thu Jul 30 23:07:31 2026] 127.0.0.1:39280 Accepted
+[Thu Jul 30 23:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:07:32 2026] 127.0.0.1:39280 [200]: GET /api/stats.php
+[Thu Jul 30 23:07:32 2026] 127.0.0.1:39280 Closing
+[Thu Jul 30 23:07:32 2026] 127.0.0.1:39282 Accepted
+[Thu Jul 30 23:07:32 2026] 127.0.0.1:39282 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:07:32 2026] 127.0.0.1:39282 Closing
+[Thu Jul 30 23:08:31 2026] 127.0.0.1:36622 Accepted
+[Thu Jul 30 23:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:08:32 2026] 127.0.0.1:36622 [200]: GET /api/stats.php
+[Thu Jul 30 23:08:32 2026] 127.0.0.1:36622 Closing
+[Thu Jul 30 23:08:32 2026] 127.0.0.1:36634 Accepted
+[Thu Jul 30 23:08:32 2026] 127.0.0.1:36634 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:08:32 2026] 127.0.0.1:36634 Closing
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36122 Accepted
+[Thu Jul 30 23:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36122 [200]: GET /api/stats.php
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36122 Closing
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36132 Accepted
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36132 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:09:32 2026] 127.0.0.1:36132 Closing
+[Thu Jul 30 23:10:31 2026] 127.0.0.1:34872 Accepted
+[Thu Jul 30 23:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:10:32 2026] 127.0.0.1:34872 [200]: GET /api/stats.php
+[Thu Jul 30 23:10:32 2026] 127.0.0.1:34872 Closing
+[Thu Jul 30 23:10:32 2026] 127.0.0.1:34888 Accepted
+[Thu Jul 30 23:10:32 2026] 127.0.0.1:34888 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:10:32 2026] 127.0.0.1:34888 Closing
+[Thu Jul 30 23:11:31 2026] 127.0.0.1:33056 Accepted
+[Thu Jul 30 23:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:11:32 2026] 127.0.0.1:33056 [200]: GET /api/stats.php
+[Thu Jul 30 23:11:32 2026] 127.0.0.1:33056 Closing
+[Thu Jul 30 23:11:32 2026] 127.0.0.1:33072 Accepted
+[Thu Jul 30 23:11:32 2026] 127.0.0.1:33072 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:11:32 2026] 127.0.0.1:33072 Closing
+[Thu Jul 30 23:12:31 2026] 127.0.0.1:58786 Accepted
+[Thu Jul 30 23:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:12:32 2026] 127.0.0.1:58786 [200]: GET /api/stats.php
+[Thu Jul 30 23:12:32 2026] 127.0.0.1:58786 Closing
+[Thu Jul 30 23:12:32 2026] 127.0.0.1:58798 Accepted
+[Thu Jul 30 23:12:32 2026] 127.0.0.1:58798 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:12:32 2026] 127.0.0.1:58798 Closing
+[Thu Jul 30 23:13:31 2026] 127.0.0.1:60964 Accepted
+[Thu Jul 30 23:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:13:32 2026] 127.0.0.1:60964 [200]: GET /api/stats.php
+[Thu Jul 30 23:13:32 2026] 127.0.0.1:60964 Closing
+[Thu Jul 30 23:13:32 2026] 127.0.0.1:60968 Accepted
+[Thu Jul 30 23:13:32 2026] 127.0.0.1:60968 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:13:32 2026] 127.0.0.1:60968 Closing
+[Thu Jul 30 23:14:31 2026] 127.0.0.1:54980 Accepted
+[Thu Jul 30 23:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:14:32 2026] 127.0.0.1:54980 [200]: GET /api/stats.php
+[Thu Jul 30 23:14:32 2026] 127.0.0.1:54980 Closing
+[Thu Jul 30 23:14:32 2026] 127.0.0.1:54982 Accepted
+[Thu Jul 30 23:14:32 2026] 127.0.0.1:54982 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:14:32 2026] 127.0.0.1:54982 Closing
+[Thu Jul 30 23:15:31 2026] 127.0.0.1:59580 Accepted
+[Thu Jul 30 23:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:15:32 2026] 127.0.0.1:59580 [200]: GET /api/stats.php
+[Thu Jul 30 23:15:32 2026] 127.0.0.1:59580 Closing
+[Thu Jul 30 23:15:32 2026] 127.0.0.1:59594 Accepted
+[Thu Jul 30 23:15:32 2026] 127.0.0.1:59594 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:15:32 2026] 127.0.0.1:59594 Closing
+[Thu Jul 30 23:16:31 2026] 127.0.0.1:36456 Accepted
+[Thu Jul 30 23:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:16:32 2026] 127.0.0.1:36456 [200]: GET /api/stats.php
+[Thu Jul 30 23:16:32 2026] 127.0.0.1:36456 Closing
+[Thu Jul 30 23:16:32 2026] 127.0.0.1:36458 Accepted
+[Thu Jul 30 23:16:32 2026] 127.0.0.1:36458 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:16:32 2026] 127.0.0.1:36458 Closing
+[Thu Jul 30 23:17:31 2026] 127.0.0.1:53916 Accepted
+[Thu Jul 30 23:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:17:32 2026] 127.0.0.1:53916 [200]: GET /api/stats.php
+[Thu Jul 30 23:17:32 2026] 127.0.0.1:53916 Closing
+[Thu Jul 30 23:17:32 2026] 127.0.0.1:53922 Accepted
+[Thu Jul 30 23:17:32 2026] 127.0.0.1:53922 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:17:32 2026] 127.0.0.1:53922 Closing
+[Thu Jul 30 23:18:31 2026] 127.0.0.1:49834 Accepted
+[Thu Jul 30 23:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:18:32 2026] 127.0.0.1:49834 [200]: GET /api/stats.php
+[Thu Jul 30 23:18:32 2026] 127.0.0.1:49834 Closing
+[Thu Jul 30 23:18:32 2026] 127.0.0.1:49850 Accepted
+[Thu Jul 30 23:18:32 2026] 127.0.0.1:49850 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:18:32 2026] 127.0.0.1:49850 Closing
+[Thu Jul 30 23:19:31 2026] 127.0.0.1:56322 Accepted
+[Thu Jul 30 23:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:19:32 2026] 127.0.0.1:56322 [200]: GET /api/stats.php
+[Thu Jul 30 23:19:32 2026] 127.0.0.1:56322 Closing
+[Thu Jul 30 23:19:32 2026] 127.0.0.1:56330 Accepted
+[Thu Jul 30 23:19:32 2026] 127.0.0.1:56330 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:19:32 2026] 127.0.0.1:56330 Closing
+[Thu Jul 30 23:20:31 2026] 127.0.0.1:53614 Accepted
+[Thu Jul 30 23:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:20:32 2026] 127.0.0.1:53614 [200]: GET /api/stats.php
+[Thu Jul 30 23:20:32 2026] 127.0.0.1:53614 Closing
+[Thu Jul 30 23:20:32 2026] 127.0.0.1:53618 Accepted
+[Thu Jul 30 23:20:32 2026] 127.0.0.1:53618 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:20:32 2026] 127.0.0.1:53618 Closing
+[Thu Jul 30 23:21:31 2026] 127.0.0.1:60976 Accepted
+[Thu Jul 30 23:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:21:32 2026] 127.0.0.1:60976 [200]: GET /api/stats.php
+[Thu Jul 30 23:21:32 2026] 127.0.0.1:60976 Closing
+[Thu Jul 30 23:21:32 2026] 127.0.0.1:60990 Accepted
+[Thu Jul 30 23:21:32 2026] 127.0.0.1:60990 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:21:32 2026] 127.0.0.1:60990 Closing
+[Thu Jul 30 23:22:31 2026] 127.0.0.1:56474 Accepted
+[Thu Jul 30 23:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:22:32 2026] 127.0.0.1:56474 [200]: GET /api/stats.php
+[Thu Jul 30 23:22:32 2026] 127.0.0.1:56474 Closing
+[Thu Jul 30 23:22:32 2026] 127.0.0.1:56480 Accepted
+[Thu Jul 30 23:22:32 2026] 127.0.0.1:56480 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:22:32 2026] 127.0.0.1:56480 Closing
+[Thu Jul 30 23:23:31 2026] 127.0.0.1:37452 Accepted
+[Thu Jul 30 23:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:23:32 2026] 127.0.0.1:37452 [200]: GET /api/stats.php
+[Thu Jul 30 23:23:32 2026] 127.0.0.1:37452 Closing
+[Thu Jul 30 23:23:32 2026] 127.0.0.1:37460 Accepted
+[Thu Jul 30 23:23:32 2026] 127.0.0.1:37460 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:23:32 2026] 127.0.0.1:37460 Closing
+[Thu Jul 30 23:24:31 2026] 127.0.0.1:52434 Accepted
+[Thu Jul 30 23:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:24:32 2026] 127.0.0.1:52434 [200]: GET /api/stats.php
+[Thu Jul 30 23:24:32 2026] 127.0.0.1:52434 Closing
+[Thu Jul 30 23:24:32 2026] 127.0.0.1:52440 Accepted
+[Thu Jul 30 23:24:32 2026] 127.0.0.1:52440 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:24:32 2026] 127.0.0.1:52440 Closing
+[Thu Jul 30 23:25:31 2026] 127.0.0.1:49736 Accepted
+[Thu Jul 30 23:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:25:32 2026] 127.0.0.1:49736 [200]: GET /api/stats.php
+[Thu Jul 30 23:25:32 2026] 127.0.0.1:49736 Closing
+[Thu Jul 30 23:25:32 2026] 127.0.0.1:49746 Accepted
+[Thu Jul 30 23:25:32 2026] 127.0.0.1:49746 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:25:32 2026] 127.0.0.1:49746 Closing
+[Thu Jul 30 23:26:31 2026] 127.0.0.1:41086 Accepted
+[Thu Jul 30 23:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:26:32 2026] 127.0.0.1:41086 [200]: GET /api/stats.php
+[Thu Jul 30 23:26:32 2026] 127.0.0.1:41086 Closing
+[Thu Jul 30 23:26:32 2026] 127.0.0.1:41096 Accepted
+[Thu Jul 30 23:26:32 2026] 127.0.0.1:41096 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:26:32 2026] 127.0.0.1:41096 Closing
+[Thu Jul 30 23:27:31 2026] 127.0.0.1:59842 Accepted
+[Thu Jul 30 23:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:27:32 2026] 127.0.0.1:59842 [200]: GET /api/stats.php
+[Thu Jul 30 23:27:32 2026] 127.0.0.1:59842 Closing
+[Thu Jul 30 23:27:32 2026] 127.0.0.1:59852 Accepted
+[Thu Jul 30 23:27:32 2026] 127.0.0.1:59852 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:27:32 2026] 127.0.0.1:59852 Closing
+[Thu Jul 30 23:28:31 2026] 127.0.0.1:54746 Accepted
+[Thu Jul 30 23:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:28:32 2026] 127.0.0.1:54746 [200]: GET /api/stats.php
+[Thu Jul 30 23:28:32 2026] 127.0.0.1:54746 Closing
+[Thu Jul 30 23:28:32 2026] 127.0.0.1:54760 Accepted
+[Thu Jul 30 23:28:32 2026] 127.0.0.1:54760 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:28:32 2026] 127.0.0.1:54760 Closing
+[Thu Jul 30 23:29:31 2026] 127.0.0.1:42516 Accepted
+[Thu Jul 30 23:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:29:32 2026] 127.0.0.1:42516 [200]: GET /api/stats.php
+[Thu Jul 30 23:29:32 2026] 127.0.0.1:42516 Closing
+[Thu Jul 30 23:29:32 2026] 127.0.0.1:42530 Accepted
+[Thu Jul 30 23:29:32 2026] 127.0.0.1:42530 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:29:32 2026] 127.0.0.1:42530 Closing
+[Thu Jul 30 23:30:31 2026] 127.0.0.1:34862 Accepted
+[Thu Jul 30 23:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:30:32 2026] 127.0.0.1:34862 [200]: GET /api/stats.php
+[Thu Jul 30 23:30:32 2026] 127.0.0.1:34862 Closing
+[Thu Jul 30 23:30:32 2026] 127.0.0.1:34870 Accepted
+[Thu Jul 30 23:30:32 2026] 127.0.0.1:34870 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:30:32 2026] 127.0.0.1:34870 Closing
+[Thu Jul 30 23:31:31 2026] 127.0.0.1:49136 Accepted
+[Thu Jul 30 23:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:31:32 2026] 127.0.0.1:49136 [200]: GET /api/stats.php
+[Thu Jul 30 23:31:32 2026] 127.0.0.1:49136 Closing
+[Thu Jul 30 23:31:32 2026] 127.0.0.1:49138 Accepted
+[Thu Jul 30 23:31:32 2026] 127.0.0.1:49138 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:31:32 2026] 127.0.0.1:49138 Closing
+[Thu Jul 30 23:32:31 2026] 127.0.0.1:46172 Accepted
+[Thu Jul 30 23:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:32:32 2026] 127.0.0.1:46172 [200]: GET /api/stats.php
+[Thu Jul 30 23:32:32 2026] 127.0.0.1:46172 Closing
+[Thu Jul 30 23:32:32 2026] 127.0.0.1:46186 Accepted
+[Thu Jul 30 23:32:32 2026] 127.0.0.1:46186 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:32:32 2026] 127.0.0.1:46186 Closing
+[Thu Jul 30 23:33:31 2026] 127.0.0.1:51702 Accepted
+[Thu Jul 30 23:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:33:32 2026] 127.0.0.1:51702 [200]: GET /api/stats.php
+[Thu Jul 30 23:33:32 2026] 127.0.0.1:51702 Closing
+[Thu Jul 30 23:33:32 2026] 127.0.0.1:51710 Accepted
+[Thu Jul 30 23:33:32 2026] 127.0.0.1:51710 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:33:32 2026] 127.0.0.1:51710 Closing
+[Thu Jul 30 23:34:31 2026] 127.0.0.1:48780 Accepted
+[Thu Jul 30 23:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:34:32 2026] 127.0.0.1:48780 [200]: GET /api/stats.php
+[Thu Jul 30 23:34:32 2026] 127.0.0.1:48780 Closing
+[Thu Jul 30 23:34:32 2026] 127.0.0.1:48792 Accepted
+[Thu Jul 30 23:34:32 2026] 127.0.0.1:48792 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:34:32 2026] 127.0.0.1:48792 Closing
+[Thu Jul 30 23:35:31 2026] 127.0.0.1:59066 Accepted
+[Thu Jul 30 23:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:35:32 2026] 127.0.0.1:59066 [200]: GET /api/stats.php
+[Thu Jul 30 23:35:32 2026] 127.0.0.1:59066 Closing
+[Thu Jul 30 23:35:32 2026] 127.0.0.1:59080 Accepted
+[Thu Jul 30 23:35:32 2026] 127.0.0.1:59080 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:35:32 2026] 127.0.0.1:59080 Closing
+[Thu Jul 30 23:36:31 2026] 127.0.0.1:35560 Accepted
+[Thu Jul 30 23:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:36:32 2026] 127.0.0.1:35560 [200]: GET /api/stats.php
+[Thu Jul 30 23:36:32 2026] 127.0.0.1:35560 Closing
+[Thu Jul 30 23:36:32 2026] 127.0.0.1:35572 Accepted
+[Thu Jul 30 23:36:32 2026] 127.0.0.1:35572 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:36:32 2026] 127.0.0.1:35572 Closing
+[Thu Jul 30 23:37:31 2026] 127.0.0.1:35826 Accepted
+[Thu Jul 30 23:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:37:32 2026] 127.0.0.1:35826 [200]: GET /api/stats.php
+[Thu Jul 30 23:37:32 2026] 127.0.0.1:35826 Closing
+[Thu Jul 30 23:37:32 2026] 127.0.0.1:35838 Accepted
+[Thu Jul 30 23:37:32 2026] 127.0.0.1:35838 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:37:32 2026] 127.0.0.1:35838 Closing
+[Thu Jul 30 23:38:31 2026] 127.0.0.1:55602 Accepted
+[Thu Jul 30 23:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:38:32 2026] 127.0.0.1:55602 [200]: GET /api/stats.php
+[Thu Jul 30 23:38:32 2026] 127.0.0.1:55602 Closing
+[Thu Jul 30 23:38:32 2026] 127.0.0.1:55608 Accepted
+[Thu Jul 30 23:38:32 2026] 127.0.0.1:55608 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:38:32 2026] 127.0.0.1:55608 Closing
+[Thu Jul 30 23:39:31 2026] 127.0.0.1:57368 Accepted
+[Thu Jul 30 23:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:39:32 2026] 127.0.0.1:57368 [200]: GET /api/stats.php
+[Thu Jul 30 23:39:32 2026] 127.0.0.1:57368 Closing
+[Thu Jul 30 23:39:32 2026] 127.0.0.1:57382 Accepted
+[Thu Jul 30 23:39:32 2026] 127.0.0.1:57382 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:39:32 2026] 127.0.0.1:57382 Closing
+[Thu Jul 30 23:40:31 2026] 127.0.0.1:40082 Accepted
+[Thu Jul 30 23:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:40:32 2026] 127.0.0.1:40082 [200]: GET /api/stats.php
+[Thu Jul 30 23:40:32 2026] 127.0.0.1:40082 Closing
+[Thu Jul 30 23:40:32 2026] 127.0.0.1:40098 Accepted
+[Thu Jul 30 23:40:32 2026] 127.0.0.1:40098 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:40:32 2026] 127.0.0.1:40098 Closing
+[Thu Jul 30 23:41:31 2026] 127.0.0.1:54466 Accepted
+[Thu Jul 30 23:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:41:32 2026] 127.0.0.1:54466 [200]: GET /api/stats.php
+[Thu Jul 30 23:41:32 2026] 127.0.0.1:54466 Closing
+[Thu Jul 30 23:41:32 2026] 127.0.0.1:54468 Accepted
+[Thu Jul 30 23:41:32 2026] 127.0.0.1:54468 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:41:32 2026] 127.0.0.1:54468 Closing
+[Thu Jul 30 23:42:31 2026] 127.0.0.1:49156 Accepted
+[Thu Jul 30 23:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:42:32 2026] 127.0.0.1:49156 [200]: GET /api/stats.php
+[Thu Jul 30 23:42:32 2026] 127.0.0.1:49156 Closing
+[Thu Jul 30 23:42:32 2026] 127.0.0.1:49172 Accepted
+[Thu Jul 30 23:42:32 2026] 127.0.0.1:49172 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:42:32 2026] 127.0.0.1:49172 Closing
+[Thu Jul 30 23:43:31 2026] 127.0.0.1:56398 Accepted
+[Thu Jul 30 23:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:43:32 2026] 127.0.0.1:56398 [200]: GET /api/stats.php
+[Thu Jul 30 23:43:32 2026] 127.0.0.1:56398 Closing
+[Thu Jul 30 23:43:32 2026] 127.0.0.1:56400 Accepted
+[Thu Jul 30 23:43:32 2026] 127.0.0.1:56400 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:43:32 2026] 127.0.0.1:56400 Closing
+[Thu Jul 30 23:44:31 2026] 127.0.0.1:52568 Accepted
+[Thu Jul 30 23:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:44:32 2026] 127.0.0.1:52568 [200]: GET /api/stats.php
+[Thu Jul 30 23:44:32 2026] 127.0.0.1:52568 Closing
+[Thu Jul 30 23:44:32 2026] 127.0.0.1:52582 Accepted
+[Thu Jul 30 23:44:32 2026] 127.0.0.1:52582 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:44:32 2026] 127.0.0.1:52582 Closing
+[Thu Jul 30 23:45:31 2026] 127.0.0.1:47992 Accepted
+[Thu Jul 30 23:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:45:32 2026] 127.0.0.1:47992 [200]: GET /api/stats.php
+[Thu Jul 30 23:45:32 2026] 127.0.0.1:47992 Closing
+[Thu Jul 30 23:45:32 2026] 127.0.0.1:47998 Accepted
+[Thu Jul 30 23:45:32 2026] 127.0.0.1:47998 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:45:32 2026] 127.0.0.1:47998 Closing
+[Thu Jul 30 23:46:31 2026] 127.0.0.1:32850 Accepted
+[Thu Jul 30 23:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:46:32 2026] 127.0.0.1:32850 [200]: GET /api/stats.php
+[Thu Jul 30 23:46:32 2026] 127.0.0.1:32850 Closing
+[Thu Jul 30 23:46:32 2026] 127.0.0.1:32852 Accepted
+[Thu Jul 30 23:46:32 2026] 127.0.0.1:32852 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:46:32 2026] 127.0.0.1:32852 Closing
+[Thu Jul 30 23:47:31 2026] 127.0.0.1:40462 Accepted
+[Thu Jul 30 23:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:47:32 2026] 127.0.0.1:40462 [200]: GET /api/stats.php
+[Thu Jul 30 23:47:32 2026] 127.0.0.1:40462 Closing
+[Thu Jul 30 23:47:32 2026] 127.0.0.1:40478 Accepted
+[Thu Jul 30 23:47:32 2026] 127.0.0.1:40478 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:47:32 2026] 127.0.0.1:40478 Closing
+[Thu Jul 30 23:48:31 2026] 127.0.0.1:40238 Accepted
+[Thu Jul 30 23:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:48:32 2026] 127.0.0.1:40238 [200]: GET /api/stats.php
+[Thu Jul 30 23:48:32 2026] 127.0.0.1:40238 Closing
+[Thu Jul 30 23:48:32 2026] 127.0.0.1:40242 Accepted
+[Thu Jul 30 23:48:32 2026] 127.0.0.1:40242 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:48:32 2026] 127.0.0.1:40242 Closing
+[Thu Jul 30 23:49:31 2026] 127.0.0.1:33944 Accepted
+[Thu Jul 30 23:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:49:32 2026] 127.0.0.1:33944 [200]: GET /api/stats.php
+[Thu Jul 30 23:49:32 2026] 127.0.0.1:33944 Closing
+[Thu Jul 30 23:49:32 2026] 127.0.0.1:33954 Accepted
+[Thu Jul 30 23:49:32 2026] 127.0.0.1:33954 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:49:32 2026] 127.0.0.1:33954 Closing
+[Thu Jul 30 23:50:31 2026] 127.0.0.1:45676 Accepted
+[Thu Jul 30 23:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:50:32 2026] 127.0.0.1:45676 [200]: GET /api/stats.php
+[Thu Jul 30 23:50:32 2026] 127.0.0.1:45676 Closing
+[Thu Jul 30 23:50:32 2026] 127.0.0.1:45678 Accepted
+[Thu Jul 30 23:50:32 2026] 127.0.0.1:45678 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:50:32 2026] 127.0.0.1:45678 Closing
+[Thu Jul 30 23:51:31 2026] 127.0.0.1:40164 Accepted
+[Thu Jul 30 23:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:51:32 2026] 127.0.0.1:40164 [200]: GET /api/stats.php
+[Thu Jul 30 23:51:32 2026] 127.0.0.1:40164 Closing
+[Thu Jul 30 23:51:32 2026] 127.0.0.1:40180 Accepted
+[Thu Jul 30 23:51:32 2026] 127.0.0.1:40180 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:51:32 2026] 127.0.0.1:40180 Closing
+[Thu Jul 30 23:52:31 2026] 127.0.0.1:59446 Accepted
+[Thu Jul 30 23:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:52:32 2026] 127.0.0.1:59446 [200]: GET /api/stats.php
+[Thu Jul 30 23:52:32 2026] 127.0.0.1:59446 Closing
+[Thu Jul 30 23:52:32 2026] 127.0.0.1:59458 Accepted
+[Thu Jul 30 23:52:32 2026] 127.0.0.1:59458 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:52:32 2026] 127.0.0.1:59458 Closing
+[Thu Jul 30 23:53:31 2026] 127.0.0.1:56040 Accepted
+[Thu Jul 30 23:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:53:32 2026] 127.0.0.1:56040 [200]: GET /api/stats.php
+[Thu Jul 30 23:53:32 2026] 127.0.0.1:56040 Closing
+[Thu Jul 30 23:53:32 2026] 127.0.0.1:56046 Accepted
+[Thu Jul 30 23:53:32 2026] 127.0.0.1:56046 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:53:32 2026] 127.0.0.1:56046 Closing
+[Thu Jul 30 23:54:31 2026] 127.0.0.1:53690 Accepted
+[Thu Jul 30 23:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:54:32 2026] 127.0.0.1:53690 [200]: GET /api/stats.php
+[Thu Jul 30 23:54:32 2026] 127.0.0.1:53690 Closing
+[Thu Jul 30 23:54:32 2026] 127.0.0.1:53694 Accepted
+[Thu Jul 30 23:54:32 2026] 127.0.0.1:53694 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:54:32 2026] 127.0.0.1:53694 Closing
+[Thu Jul 30 23:55:31 2026] 127.0.0.1:54524 Accepted
+[Thu Jul 30 23:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:55:32 2026] 127.0.0.1:54524 [200]: GET /api/stats.php
+[Thu Jul 30 23:55:32 2026] 127.0.0.1:54524 Closing
+[Thu Jul 30 23:55:32 2026] 127.0.0.1:54530 Accepted
+[Thu Jul 30 23:55:32 2026] 127.0.0.1:54530 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:55:32 2026] 127.0.0.1:54530 Closing
+[Thu Jul 30 23:56:31 2026] 127.0.0.1:40290 Accepted
+[Thu Jul 30 23:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:56:32 2026] 127.0.0.1:40290 [200]: GET /api/stats.php
+[Thu Jul 30 23:56:32 2026] 127.0.0.1:40290 Closing
+[Thu Jul 30 23:56:32 2026] 127.0.0.1:40306 Accepted
+[Thu Jul 30 23:56:32 2026] 127.0.0.1:40306 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:56:32 2026] 127.0.0.1:40306 Closing
+[Thu Jul 30 23:57:31 2026] 127.0.0.1:52868 Accepted
+[Thu Jul 30 23:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:57:32 2026] 127.0.0.1:52868 [200]: GET /api/stats.php
+[Thu Jul 30 23:57:32 2026] 127.0.0.1:52868 Closing
+[Thu Jul 30 23:57:32 2026] 127.0.0.1:52878 Accepted
+[Thu Jul 30 23:57:32 2026] 127.0.0.1:52878 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:57:32 2026] 127.0.0.1:52878 Closing
+[Thu Jul 30 23:58:31 2026] 127.0.0.1:48538 Accepted
+[Thu Jul 30 23:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:58:32 2026] 127.0.0.1:48538 [200]: GET /api/stats.php
+[Thu Jul 30 23:58:32 2026] 127.0.0.1:48538 Closing
+[Thu Jul 30 23:58:32 2026] 127.0.0.1:48554 Accepted
+[Thu Jul 30 23:58:32 2026] 127.0.0.1:48554 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:58:32 2026] 127.0.0.1:48554 Closing
+[Thu Jul 30 23:59:31 2026] 127.0.0.1:57284 Accepted
+[Thu Jul 30 23:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Thu Jul 30 23:59:32 2026] 127.0.0.1:57284 [200]: GET /api/stats.php
+[Thu Jul 30 23:59:32 2026] 127.0.0.1:57284 Closing
+[Thu Jul 30 23:59:32 2026] 127.0.0.1:57290 Accepted
+[Thu Jul 30 23:59:32 2026] 127.0.0.1:57290 [200]: GET /api/chart.php?range=year
+[Thu Jul 30 23:59:32 2026] 127.0.0.1:57290 Closing
+[Fri Jul 31 00:00:31 2026] 127.0.0.1:33416 Accepted
+[Fri Jul 31 00:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:00:32 2026] 127.0.0.1:33416 [200]: GET /api/stats.php
+[Fri Jul 31 00:00:32 2026] 127.0.0.1:33416 Closing
+[Fri Jul 31 00:00:32 2026] 127.0.0.1:33424 Accepted
+[Fri Jul 31 00:00:32 2026] 127.0.0.1:33424 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:00:32 2026] 127.0.0.1:33424 Closing
+[Fri Jul 31 00:01:31 2026] 127.0.0.1:47404 Accepted
+[Fri Jul 31 00:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:01:32 2026] 127.0.0.1:47404 [200]: GET /api/stats.php
+[Fri Jul 31 00:01:32 2026] 127.0.0.1:47404 Closing
+[Fri Jul 31 00:01:32 2026] 127.0.0.1:47410 Accepted
+[Fri Jul 31 00:01:32 2026] 127.0.0.1:47410 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:01:32 2026] 127.0.0.1:47410 Closing
+[Fri Jul 31 00:02:31 2026] 127.0.0.1:53076 Accepted
+[Fri Jul 31 00:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:02:32 2026] 127.0.0.1:53076 [200]: GET /api/stats.php
+[Fri Jul 31 00:02:32 2026] 127.0.0.1:53076 Closing
+[Fri Jul 31 00:02:32 2026] 127.0.0.1:53080 Accepted
+[Fri Jul 31 00:02:32 2026] 127.0.0.1:53080 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:02:32 2026] 127.0.0.1:53080 Closing
+[Fri Jul 31 00:03:31 2026] 127.0.0.1:48072 Accepted
+[Fri Jul 31 00:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:03:32 2026] 127.0.0.1:48072 [200]: GET /api/stats.php
+[Fri Jul 31 00:03:32 2026] 127.0.0.1:48072 Closing
+[Fri Jul 31 00:03:32 2026] 127.0.0.1:48088 Accepted
+[Fri Jul 31 00:03:32 2026] 127.0.0.1:48088 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:03:32 2026] 127.0.0.1:48088 Closing
+[Fri Jul 31 00:04:31 2026] 127.0.0.1:58908 Accepted
+[Fri Jul 31 00:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:04:32 2026] 127.0.0.1:58908 [200]: GET /api/stats.php
+[Fri Jul 31 00:04:32 2026] 127.0.0.1:58908 Closing
+[Fri Jul 31 00:04:32 2026] 127.0.0.1:58922 Accepted
+[Fri Jul 31 00:04:32 2026] 127.0.0.1:58922 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:04:32 2026] 127.0.0.1:58922 Closing
+[Fri Jul 31 00:05:31 2026] 127.0.0.1:56476 Accepted
+[Fri Jul 31 00:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:05:32 2026] 127.0.0.1:56476 [200]: GET /api/stats.php
+[Fri Jul 31 00:05:32 2026] 127.0.0.1:56476 Closing
+[Fri Jul 31 00:05:32 2026] 127.0.0.1:56478 Accepted
+[Fri Jul 31 00:05:32 2026] 127.0.0.1:56478 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:05:32 2026] 127.0.0.1:56478 Closing
+[Fri Jul 31 00:06:31 2026] 127.0.0.1:42916 Accepted
+[Fri Jul 31 00:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:06:32 2026] 127.0.0.1:42916 [200]: GET /api/stats.php
+[Fri Jul 31 00:06:32 2026] 127.0.0.1:42916 Closing
+[Fri Jul 31 00:06:32 2026] 127.0.0.1:42922 Accepted
+[Fri Jul 31 00:06:32 2026] 127.0.0.1:42922 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:06:32 2026] 127.0.0.1:42922 Closing
+[Fri Jul 31 00:07:31 2026] 127.0.0.1:34384 Accepted
+[Fri Jul 31 00:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:07:32 2026] 127.0.0.1:34384 [200]: GET /api/stats.php
+[Fri Jul 31 00:07:32 2026] 127.0.0.1:34384 Closing
+[Fri Jul 31 00:07:32 2026] 127.0.0.1:34388 Accepted
+[Fri Jul 31 00:07:32 2026] 127.0.0.1:34388 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:07:32 2026] 127.0.0.1:34388 Closing
+[Fri Jul 31 00:08:31 2026] 127.0.0.1:38402 Accepted
+[Fri Jul 31 00:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:08:32 2026] 127.0.0.1:38402 [200]: GET /api/stats.php
+[Fri Jul 31 00:08:32 2026] 127.0.0.1:38402 Closing
+[Fri Jul 31 00:08:32 2026] 127.0.0.1:38416 Accepted
+[Fri Jul 31 00:08:32 2026] 127.0.0.1:38416 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:08:32 2026] 127.0.0.1:38416 Closing
+[Fri Jul 31 00:09:31 2026] 127.0.0.1:41392 Accepted
+[Fri Jul 31 00:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:09:32 2026] 127.0.0.1:41392 [200]: GET /api/stats.php
+[Fri Jul 31 00:09:32 2026] 127.0.0.1:41392 Closing
+[Fri Jul 31 00:09:32 2026] 127.0.0.1:41404 Accepted
+[Fri Jul 31 00:09:32 2026] 127.0.0.1:41404 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:09:32 2026] 127.0.0.1:41404 Closing
+[Fri Jul 31 00:10:31 2026] 127.0.0.1:40982 Accepted
+[Fri Jul 31 00:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:10:32 2026] 127.0.0.1:40982 [200]: GET /api/stats.php
+[Fri Jul 31 00:10:32 2026] 127.0.0.1:40982 Closing
+[Fri Jul 31 00:10:32 2026] 127.0.0.1:40992 Accepted
+[Fri Jul 31 00:10:32 2026] 127.0.0.1:40992 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:10:32 2026] 127.0.0.1:40992 Closing
+[Fri Jul 31 00:11:31 2026] 127.0.0.1:39760 Accepted
+[Fri Jul 31 00:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:11:32 2026] 127.0.0.1:39760 [200]: GET /api/stats.php
+[Fri Jul 31 00:11:32 2026] 127.0.0.1:39760 Closing
+[Fri Jul 31 00:11:32 2026] 127.0.0.1:39764 Accepted
+[Fri Jul 31 00:11:32 2026] 127.0.0.1:39764 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:11:32 2026] 127.0.0.1:39764 Closing
+[Fri Jul 31 00:12:31 2026] 127.0.0.1:59976 Accepted
+[Fri Jul 31 00:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:12:32 2026] 127.0.0.1:59976 [200]: GET /api/stats.php
+[Fri Jul 31 00:12:32 2026] 127.0.0.1:59976 Closing
+[Fri Jul 31 00:12:32 2026] 127.0.0.1:59984 Accepted
+[Fri Jul 31 00:12:32 2026] 127.0.0.1:59984 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:12:32 2026] 127.0.0.1:59984 Closing
+[Fri Jul 31 00:13:31 2026] 127.0.0.1:41610 Accepted
+[Fri Jul 31 00:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:13:32 2026] 127.0.0.1:41610 [200]: GET /api/stats.php
+[Fri Jul 31 00:13:32 2026] 127.0.0.1:41610 Closing
+[Fri Jul 31 00:13:32 2026] 127.0.0.1:41618 Accepted
+[Fri Jul 31 00:13:32 2026] 127.0.0.1:41618 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:13:32 2026] 127.0.0.1:41618 Closing
+[Fri Jul 31 00:14:31 2026] 127.0.0.1:58088 Accepted
+[Fri Jul 31 00:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:14:32 2026] 127.0.0.1:58088 [200]: GET /api/stats.php
+[Fri Jul 31 00:14:32 2026] 127.0.0.1:58088 Closing
+[Fri Jul 31 00:14:32 2026] 127.0.0.1:58104 Accepted
+[Fri Jul 31 00:14:32 2026] 127.0.0.1:58104 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:14:32 2026] 127.0.0.1:58104 Closing
+[Fri Jul 31 00:15:31 2026] 127.0.0.1:32976 Accepted
+[Fri Jul 31 00:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:15:32 2026] 127.0.0.1:32976 [200]: GET /api/stats.php
+[Fri Jul 31 00:15:32 2026] 127.0.0.1:32976 Closing
+[Fri Jul 31 00:15:32 2026] 127.0.0.1:32992 Accepted
+[Fri Jul 31 00:15:32 2026] 127.0.0.1:32992 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:15:32 2026] 127.0.0.1:32992 Closing
+[Fri Jul 31 00:16:31 2026] 127.0.0.1:33398 Accepted
+[Fri Jul 31 00:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:16:32 2026] 127.0.0.1:33398 [200]: GET /api/stats.php
+[Fri Jul 31 00:16:32 2026] 127.0.0.1:33398 Closing
+[Fri Jul 31 00:16:32 2026] 127.0.0.1:33404 Accepted
+[Fri Jul 31 00:16:32 2026] 127.0.0.1:33404 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:16:32 2026] 127.0.0.1:33404 Closing
+[Fri Jul 31 00:17:31 2026] 127.0.0.1:46532 Accepted
+[Fri Jul 31 00:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:17:32 2026] 127.0.0.1:46532 [200]: GET /api/stats.php
+[Fri Jul 31 00:17:32 2026] 127.0.0.1:46532 Closing
+[Fri Jul 31 00:17:32 2026] 127.0.0.1:46538 Accepted
+[Fri Jul 31 00:17:32 2026] 127.0.0.1:46538 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:17:32 2026] 127.0.0.1:46538 Closing
+[Fri Jul 31 00:18:31 2026] 127.0.0.1:59426 Accepted
+[Fri Jul 31 00:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:18:32 2026] 127.0.0.1:59426 [200]: GET /api/stats.php
+[Fri Jul 31 00:18:32 2026] 127.0.0.1:59426 Closing
+[Fri Jul 31 00:18:32 2026] 127.0.0.1:59442 Accepted
+[Fri Jul 31 00:18:32 2026] 127.0.0.1:59442 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:18:32 2026] 127.0.0.1:59442 Closing
+[Fri Jul 31 00:19:31 2026] 127.0.0.1:58340 Accepted
+[Fri Jul 31 00:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:19:32 2026] 127.0.0.1:58340 [200]: GET /api/stats.php
+[Fri Jul 31 00:19:32 2026] 127.0.0.1:58340 Closing
+[Fri Jul 31 00:19:32 2026] 127.0.0.1:58350 Accepted
+[Fri Jul 31 00:19:32 2026] 127.0.0.1:58350 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:19:32 2026] 127.0.0.1:58350 Closing
+[Fri Jul 31 00:20:31 2026] 127.0.0.1:38530 Accepted
+[Fri Jul 31 00:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:20:32 2026] 127.0.0.1:38530 [200]: GET /api/stats.php
+[Fri Jul 31 00:20:32 2026] 127.0.0.1:38530 Closing
+[Fri Jul 31 00:20:32 2026] 127.0.0.1:38538 Accepted
+[Fri Jul 31 00:20:32 2026] 127.0.0.1:38538 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:20:32 2026] 127.0.0.1:38538 Closing
+[Fri Jul 31 00:21:31 2026] 127.0.0.1:34546 Accepted
+[Fri Jul 31 00:21:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:21:32 2026] 127.0.0.1:34546 [200]: GET /api/stats.php
+[Fri Jul 31 00:21:32 2026] 127.0.0.1:34546 Closing
+[Fri Jul 31 00:21:32 2026] 127.0.0.1:34556 Accepted
+[Fri Jul 31 00:21:32 2026] 127.0.0.1:34556 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:21:32 2026] 127.0.0.1:34556 Closing
+[Fri Jul 31 00:22:31 2026] 127.0.0.1:37260 Accepted
+[Fri Jul 31 00:22:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:22:32 2026] 127.0.0.1:37260 [200]: GET /api/stats.php
+[Fri Jul 31 00:22:32 2026] 127.0.0.1:37260 Closing
+[Fri Jul 31 00:22:32 2026] 127.0.0.1:37270 Accepted
+[Fri Jul 31 00:22:32 2026] 127.0.0.1:37270 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:22:32 2026] 127.0.0.1:37270 Closing
+[Fri Jul 31 00:23:31 2026] 127.0.0.1:33440 Accepted
+[Fri Jul 31 00:23:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:23:32 2026] 127.0.0.1:33440 [200]: GET /api/stats.php
+[Fri Jul 31 00:23:32 2026] 127.0.0.1:33440 Closing
+[Fri Jul 31 00:23:32 2026] 127.0.0.1:33456 Accepted
+[Fri Jul 31 00:23:32 2026] 127.0.0.1:33456 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:23:32 2026] 127.0.0.1:33456 Closing
+[Fri Jul 31 00:24:31 2026] 127.0.0.1:41980 Accepted
+[Fri Jul 31 00:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:24:32 2026] 127.0.0.1:41980 [200]: GET /api/stats.php
+[Fri Jul 31 00:24:32 2026] 127.0.0.1:41980 Closing
+[Fri Jul 31 00:24:32 2026] 127.0.0.1:41986 Accepted
+[Fri Jul 31 00:24:32 2026] 127.0.0.1:41986 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:24:32 2026] 127.0.0.1:41986 Closing
+[Fri Jul 31 00:25:31 2026] 127.0.0.1:52812 Accepted
+[Fri Jul 31 00:25:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:25:32 2026] 127.0.0.1:52812 [200]: GET /api/stats.php
+[Fri Jul 31 00:25:32 2026] 127.0.0.1:52812 Closing
+[Fri Jul 31 00:25:32 2026] 127.0.0.1:52824 Accepted
+[Fri Jul 31 00:25:32 2026] 127.0.0.1:52824 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:25:32 2026] 127.0.0.1:52824 Closing
+[Fri Jul 31 00:26:31 2026] 127.0.0.1:37802 Accepted
+[Fri Jul 31 00:26:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:26:32 2026] 127.0.0.1:37802 [200]: GET /api/stats.php
+[Fri Jul 31 00:26:32 2026] 127.0.0.1:37802 Closing
+[Fri Jul 31 00:26:32 2026] 127.0.0.1:37806 Accepted
+[Fri Jul 31 00:26:32 2026] 127.0.0.1:37806 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:26:32 2026] 127.0.0.1:37806 Closing
+[Fri Jul 31 00:27:31 2026] 127.0.0.1:36516 Accepted
+[Fri Jul 31 00:27:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:27:32 2026] 127.0.0.1:36516 [200]: GET /api/stats.php
+[Fri Jul 31 00:27:32 2026] 127.0.0.1:36516 Closing
+[Fri Jul 31 00:27:32 2026] 127.0.0.1:36528 Accepted
+[Fri Jul 31 00:27:32 2026] 127.0.0.1:36528 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:27:32 2026] 127.0.0.1:36528 Closing
+[Fri Jul 31 00:28:31 2026] 127.0.0.1:53820 Accepted
+[Fri Jul 31 00:28:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:28:32 2026] 127.0.0.1:53820 [200]: GET /api/stats.php
+[Fri Jul 31 00:28:32 2026] 127.0.0.1:53820 Closing
+[Fri Jul 31 00:28:32 2026] 127.0.0.1:53834 Accepted
+[Fri Jul 31 00:28:32 2026] 127.0.0.1:53834 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:28:32 2026] 127.0.0.1:53834 Closing
+[Fri Jul 31 00:29:31 2026] 127.0.0.1:46054 Accepted
+[Fri Jul 31 00:29:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:29:32 2026] 127.0.0.1:46054 [200]: GET /api/stats.php
+[Fri Jul 31 00:29:32 2026] 127.0.0.1:46054 Closing
+[Fri Jul 31 00:29:32 2026] 127.0.0.1:46064 Accepted
+[Fri Jul 31 00:29:32 2026] 127.0.0.1:46064 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:29:32 2026] 127.0.0.1:46064 Closing
+[Fri Jul 31 00:30:31 2026] 127.0.0.1:50068 Accepted
+[Fri Jul 31 00:30:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:30:32 2026] 127.0.0.1:50068 [200]: GET /api/stats.php
+[Fri Jul 31 00:30:32 2026] 127.0.0.1:50068 Closing
+[Fri Jul 31 00:30:32 2026] 127.0.0.1:50070 Accepted
+[Fri Jul 31 00:30:32 2026] 127.0.0.1:50070 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:30:32 2026] 127.0.0.1:50070 Closing
+[Fri Jul 31 00:31:31 2026] 127.0.0.1:51078 Accepted
+[Fri Jul 31 00:31:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:31:32 2026] 127.0.0.1:51078 [200]: GET /api/stats.php
+[Fri Jul 31 00:31:32 2026] 127.0.0.1:51078 Closing
+[Fri Jul 31 00:31:32 2026] 127.0.0.1:51082 Accepted
+[Fri Jul 31 00:31:32 2026] 127.0.0.1:51082 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:31:32 2026] 127.0.0.1:51082 Closing
+[Fri Jul 31 00:32:31 2026] 127.0.0.1:42608 Accepted
+[Fri Jul 31 00:32:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:32:32 2026] 127.0.0.1:42608 [200]: GET /api/stats.php
+[Fri Jul 31 00:32:32 2026] 127.0.0.1:42608 Closing
+[Fri Jul 31 00:32:32 2026] 127.0.0.1:42624 Accepted
+[Fri Jul 31 00:32:32 2026] 127.0.0.1:42624 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:32:32 2026] 127.0.0.1:42624 Closing
+[Fri Jul 31 00:33:31 2026] 127.0.0.1:37354 Accepted
+[Fri Jul 31 00:33:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:33:32 2026] 127.0.0.1:37354 [200]: GET /api/stats.php
+[Fri Jul 31 00:33:32 2026] 127.0.0.1:37354 Closing
+[Fri Jul 31 00:33:32 2026] 127.0.0.1:37366 Accepted
+[Fri Jul 31 00:33:32 2026] 127.0.0.1:37366 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:33:32 2026] 127.0.0.1:37366 Closing
+[Fri Jul 31 00:34:31 2026] 127.0.0.1:43648 Accepted
+[Fri Jul 31 00:34:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:34:32 2026] 127.0.0.1:43648 [200]: GET /api/stats.php
+[Fri Jul 31 00:34:32 2026] 127.0.0.1:43648 Closing
+[Fri Jul 31 00:34:32 2026] 127.0.0.1:43664 Accepted
+[Fri Jul 31 00:34:32 2026] 127.0.0.1:43664 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:34:32 2026] 127.0.0.1:43664 Closing
+[Fri Jul 31 00:35:31 2026] 127.0.0.1:38900 Accepted
+[Fri Jul 31 00:35:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:35:32 2026] 127.0.0.1:38900 [200]: GET /api/stats.php
+[Fri Jul 31 00:35:32 2026] 127.0.0.1:38900 Closing
+[Fri Jul 31 00:35:32 2026] 127.0.0.1:38916 Accepted
+[Fri Jul 31 00:35:32 2026] 127.0.0.1:38916 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:35:32 2026] 127.0.0.1:38916 Closing
+[Fri Jul 31 00:36:31 2026] 127.0.0.1:47788 Accepted
+[Fri Jul 31 00:36:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:36:32 2026] 127.0.0.1:47788 [200]: GET /api/stats.php
+[Fri Jul 31 00:36:32 2026] 127.0.0.1:47788 Closing
+[Fri Jul 31 00:36:32 2026] 127.0.0.1:47790 Accepted
+[Fri Jul 31 00:36:32 2026] 127.0.0.1:47790 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:36:32 2026] 127.0.0.1:47790 Closing
+[Fri Jul 31 00:37:31 2026] 127.0.0.1:39186 Accepted
+[Fri Jul 31 00:37:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:37:32 2026] 127.0.0.1:39186 [200]: GET /api/stats.php
+[Fri Jul 31 00:37:32 2026] 127.0.0.1:39186 Closing
+[Fri Jul 31 00:37:32 2026] 127.0.0.1:39198 Accepted
+[Fri Jul 31 00:37:32 2026] 127.0.0.1:39198 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:37:32 2026] 127.0.0.1:39198 Closing
+[Fri Jul 31 00:38:31 2026] 127.0.0.1:55946 Accepted
+[Fri Jul 31 00:38:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:38:32 2026] 127.0.0.1:55946 [200]: GET /api/stats.php
+[Fri Jul 31 00:38:32 2026] 127.0.0.1:55946 Closing
+[Fri Jul 31 00:38:32 2026] 127.0.0.1:55954 Accepted
+[Fri Jul 31 00:38:32 2026] 127.0.0.1:55954 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:38:32 2026] 127.0.0.1:55954 Closing
+[Fri Jul 31 00:39:31 2026] 127.0.0.1:55694 Accepted
+[Fri Jul 31 00:39:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:39:32 2026] 127.0.0.1:55694 [200]: GET /api/stats.php
+[Fri Jul 31 00:39:32 2026] 127.0.0.1:55694 Closing
+[Fri Jul 31 00:39:32 2026] 127.0.0.1:55698 Accepted
+[Fri Jul 31 00:39:32 2026] 127.0.0.1:55698 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:39:32 2026] 127.0.0.1:55698 Closing
+[Fri Jul 31 00:40:31 2026] 127.0.0.1:48296 Accepted
+[Fri Jul 31 00:40:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:40:32 2026] 127.0.0.1:48296 [200]: GET /api/stats.php
+[Fri Jul 31 00:40:32 2026] 127.0.0.1:48296 Closing
+[Fri Jul 31 00:40:32 2026] 127.0.0.1:48298 Accepted
+[Fri Jul 31 00:40:32 2026] 127.0.0.1:48298 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:40:32 2026] 127.0.0.1:48298 Closing
+[Fri Jul 31 00:41:31 2026] 127.0.0.1:52996 Accepted
+[Fri Jul 31 00:41:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:41:32 2026] 127.0.0.1:52996 [200]: GET /api/stats.php
+[Fri Jul 31 00:41:32 2026] 127.0.0.1:52996 Closing
+[Fri Jul 31 00:41:32 2026] 127.0.0.1:53006 Accepted
+[Fri Jul 31 00:41:32 2026] 127.0.0.1:53006 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:41:32 2026] 127.0.0.1:53006 Closing
+[Fri Jul 31 00:42:31 2026] 127.0.0.1:56106 Accepted
+[Fri Jul 31 00:42:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:42:32 2026] 127.0.0.1:56106 [200]: GET /api/stats.php
+[Fri Jul 31 00:42:32 2026] 127.0.0.1:56106 Closing
+[Fri Jul 31 00:42:32 2026] 127.0.0.1:56112 Accepted
+[Fri Jul 31 00:42:32 2026] 127.0.0.1:56112 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:42:32 2026] 127.0.0.1:56112 Closing
+[Fri Jul 31 00:43:31 2026] 127.0.0.1:51224 Accepted
+[Fri Jul 31 00:43:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:43:32 2026] 127.0.0.1:51224 [200]: GET /api/stats.php
+[Fri Jul 31 00:43:32 2026] 127.0.0.1:51224 Closing
+[Fri Jul 31 00:43:32 2026] 127.0.0.1:51228 Accepted
+[Fri Jul 31 00:43:32 2026] 127.0.0.1:51228 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:43:32 2026] 127.0.0.1:51228 Closing
+[Fri Jul 31 00:44:31 2026] 127.0.0.1:52152 Accepted
+[Fri Jul 31 00:44:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:44:32 2026] 127.0.0.1:52152 [200]: GET /api/stats.php
+[Fri Jul 31 00:44:32 2026] 127.0.0.1:52152 Closing
+[Fri Jul 31 00:44:32 2026] 127.0.0.1:52168 Accepted
+[Fri Jul 31 00:44:32 2026] 127.0.0.1:52168 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:44:32 2026] 127.0.0.1:52168 Closing
+[Fri Jul 31 00:45:31 2026] 127.0.0.1:50032 Accepted
+[Fri Jul 31 00:45:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:45:32 2026] 127.0.0.1:50032 [200]: GET /api/stats.php
+[Fri Jul 31 00:45:32 2026] 127.0.0.1:50032 Closing
+[Fri Jul 31 00:45:32 2026] 127.0.0.1:50038 Accepted
+[Fri Jul 31 00:45:32 2026] 127.0.0.1:50038 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:45:32 2026] 127.0.0.1:50038 Closing
+[Fri Jul 31 00:46:31 2026] 127.0.0.1:41130 Accepted
+[Fri Jul 31 00:46:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:46:32 2026] 127.0.0.1:41130 [200]: GET /api/stats.php
+[Fri Jul 31 00:46:32 2026] 127.0.0.1:41130 Closing
+[Fri Jul 31 00:46:32 2026] 127.0.0.1:41142 Accepted
+[Fri Jul 31 00:46:32 2026] 127.0.0.1:41142 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:46:32 2026] 127.0.0.1:41142 Closing
+[Fri Jul 31 00:47:31 2026] 127.0.0.1:35098 Accepted
+[Fri Jul 31 00:47:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:47:32 2026] 127.0.0.1:35098 [200]: GET /api/stats.php
+[Fri Jul 31 00:47:32 2026] 127.0.0.1:35098 Closing
+[Fri Jul 31 00:47:32 2026] 127.0.0.1:35114 Accepted
+[Fri Jul 31 00:47:32 2026] 127.0.0.1:35114 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:47:32 2026] 127.0.0.1:35114 Closing
+[Fri Jul 31 00:48:31 2026] 127.0.0.1:42010 Accepted
+[Fri Jul 31 00:48:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:48:32 2026] 127.0.0.1:42010 [200]: GET /api/stats.php
+[Fri Jul 31 00:48:32 2026] 127.0.0.1:42010 Closing
+[Fri Jul 31 00:48:32 2026] 127.0.0.1:42012 Accepted
+[Fri Jul 31 00:48:32 2026] 127.0.0.1:42012 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:48:32 2026] 127.0.0.1:42012 Closing
+[Fri Jul 31 00:49:31 2026] 127.0.0.1:42896 Accepted
+[Fri Jul 31 00:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:49:32 2026] 127.0.0.1:42896 [200]: GET /api/stats.php
+[Fri Jul 31 00:49:32 2026] 127.0.0.1:42896 Closing
+[Fri Jul 31 00:49:32 2026] 127.0.0.1:42910 Accepted
+[Fri Jul 31 00:49:32 2026] 127.0.0.1:42910 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:49:32 2026] 127.0.0.1:42910 Closing
+[Fri Jul 31 00:50:31 2026] 127.0.0.1:41750 Accepted
+[Fri Jul 31 00:50:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:50:32 2026] 127.0.0.1:41750 [200]: GET /api/stats.php
+[Fri Jul 31 00:50:32 2026] 127.0.0.1:41750 Closing
+[Fri Jul 31 00:50:32 2026] 127.0.0.1:41758 Accepted
+[Fri Jul 31 00:50:32 2026] 127.0.0.1:41758 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:50:32 2026] 127.0.0.1:41758 Closing
+[Fri Jul 31 00:51:31 2026] 127.0.0.1:50508 Accepted
+[Fri Jul 31 00:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:51:32 2026] 127.0.0.1:50508 [200]: GET /api/stats.php
+[Fri Jul 31 00:51:32 2026] 127.0.0.1:50508 Closing
+[Fri Jul 31 00:51:32 2026] 127.0.0.1:50520 Accepted
+[Fri Jul 31 00:51:32 2026] 127.0.0.1:50520 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:51:32 2026] 127.0.0.1:50520 Closing
+[Fri Jul 31 00:52:31 2026] 127.0.0.1:56036 Accepted
+[Fri Jul 31 00:52:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:52:32 2026] 127.0.0.1:56036 [200]: GET /api/stats.php
+[Fri Jul 31 00:52:32 2026] 127.0.0.1:56036 Closing
+[Fri Jul 31 00:52:32 2026] 127.0.0.1:56044 Accepted
+[Fri Jul 31 00:52:32 2026] 127.0.0.1:56044 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:52:32 2026] 127.0.0.1:56044 Closing
+[Fri Jul 31 00:53:31 2026] 127.0.0.1:52966 Accepted
+[Fri Jul 31 00:53:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:53:32 2026] 127.0.0.1:52966 [200]: GET /api/stats.php
+[Fri Jul 31 00:53:32 2026] 127.0.0.1:52966 Closing
+[Fri Jul 31 00:53:32 2026] 127.0.0.1:52976 Accepted
+[Fri Jul 31 00:53:32 2026] 127.0.0.1:52976 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:53:32 2026] 127.0.0.1:52976 Closing
+[Fri Jul 31 00:54:31 2026] 127.0.0.1:46328 Accepted
+[Fri Jul 31 00:54:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:54:32 2026] 127.0.0.1:46328 [200]: GET /api/stats.php
+[Fri Jul 31 00:54:32 2026] 127.0.0.1:46328 Closing
+[Fri Jul 31 00:54:32 2026] 127.0.0.1:46340 Accepted
+[Fri Jul 31 00:54:32 2026] 127.0.0.1:46340 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:54:32 2026] 127.0.0.1:46340 Closing
+[Fri Jul 31 00:55:31 2026] 127.0.0.1:46076 Accepted
+[Fri Jul 31 00:55:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:55:32 2026] 127.0.0.1:46076 [200]: GET /api/stats.php
+[Fri Jul 31 00:55:32 2026] 127.0.0.1:46076 Closing
+[Fri Jul 31 00:55:32 2026] 127.0.0.1:46092 Accepted
+[Fri Jul 31 00:55:32 2026] 127.0.0.1:46092 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:55:32 2026] 127.0.0.1:46092 Closing
+[Fri Jul 31 00:56:31 2026] 127.0.0.1:60806 Accepted
+[Fri Jul 31 00:56:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:56:32 2026] 127.0.0.1:60806 [200]: GET /api/stats.php
+[Fri Jul 31 00:56:32 2026] 127.0.0.1:60806 Closing
+[Fri Jul 31 00:56:32 2026] 127.0.0.1:60822 Accepted
+[Fri Jul 31 00:56:32 2026] 127.0.0.1:60822 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:56:32 2026] 127.0.0.1:60822 Closing
+[Fri Jul 31 00:57:31 2026] 127.0.0.1:57662 Accepted
+[Fri Jul 31 00:57:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:57:32 2026] 127.0.0.1:57662 [200]: GET /api/stats.php
+[Fri Jul 31 00:57:32 2026] 127.0.0.1:57662 Closing
+[Fri Jul 31 00:57:32 2026] 127.0.0.1:57676 Accepted
+[Fri Jul 31 00:57:32 2026] 127.0.0.1:57676 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:57:32 2026] 127.0.0.1:57676 Closing
+[Fri Jul 31 00:58:31 2026] 127.0.0.1:57304 Accepted
+[Fri Jul 31 00:58:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:58:32 2026] 127.0.0.1:57304 [200]: GET /api/stats.php
+[Fri Jul 31 00:58:32 2026] 127.0.0.1:57304 Closing
+[Fri Jul 31 00:58:32 2026] 127.0.0.1:57316 Accepted
+[Fri Jul 31 00:58:32 2026] 127.0.0.1:57316 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:58:32 2026] 127.0.0.1:57316 Closing
+[Fri Jul 31 00:59:31 2026] 127.0.0.1:40804 Accepted
+[Fri Jul 31 00:59:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 00:59:32 2026] 127.0.0.1:40804 [200]: GET /api/stats.php
+[Fri Jul 31 00:59:32 2026] 127.0.0.1:40804 Closing
+[Fri Jul 31 00:59:32 2026] 127.0.0.1:40816 Accepted
+[Fri Jul 31 00:59:32 2026] 127.0.0.1:40816 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 00:59:32 2026] 127.0.0.1:40816 Closing
+[Fri Jul 31 01:00:31 2026] 127.0.0.1:58474 Accepted
+[Fri Jul 31 01:00:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:00:32 2026] 127.0.0.1:58474 [200]: GET /api/stats.php
+[Fri Jul 31 01:00:32 2026] 127.0.0.1:58474 Closing
+[Fri Jul 31 01:00:32 2026] 127.0.0.1:58478 Accepted
+[Fri Jul 31 01:00:32 2026] 127.0.0.1:58478 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:00:32 2026] 127.0.0.1:58478 Closing
+[Fri Jul 31 01:01:31 2026] 127.0.0.1:41288 Accepted
+[Fri Jul 31 01:01:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:01:32 2026] 127.0.0.1:41288 [200]: GET /api/stats.php
+[Fri Jul 31 01:01:32 2026] 127.0.0.1:41288 Closing
+[Fri Jul 31 01:01:32 2026] 127.0.0.1:41298 Accepted
+[Fri Jul 31 01:01:32 2026] 127.0.0.1:41298 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:01:32 2026] 127.0.0.1:41298 Closing
+[Fri Jul 31 01:02:31 2026] 127.0.0.1:37016 Accepted
+[Fri Jul 31 01:02:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:02:32 2026] 127.0.0.1:37016 [200]: GET /api/stats.php
+[Fri Jul 31 01:02:32 2026] 127.0.0.1:37016 Closing
+[Fri Jul 31 01:02:32 2026] 127.0.0.1:37028 Accepted
+[Fri Jul 31 01:02:32 2026] 127.0.0.1:37028 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:02:32 2026] 127.0.0.1:37028 Closing
+[Fri Jul 31 01:03:31 2026] 127.0.0.1:42792 Accepted
+[Fri Jul 31 01:03:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:03:32 2026] 127.0.0.1:42792 [200]: GET /api/stats.php
+[Fri Jul 31 01:03:32 2026] 127.0.0.1:42792 Closing
+[Fri Jul 31 01:03:32 2026] 127.0.0.1:42798 Accepted
+[Fri Jul 31 01:03:32 2026] 127.0.0.1:42798 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:03:32 2026] 127.0.0.1:42798 Closing
+[Fri Jul 31 01:04:31 2026] 127.0.0.1:52942 Accepted
+[Fri Jul 31 01:04:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:04:32 2026] 127.0.0.1:52942 [200]: GET /api/stats.php
+[Fri Jul 31 01:04:32 2026] 127.0.0.1:52942 Closing
+[Fri Jul 31 01:04:32 2026] 127.0.0.1:52958 Accepted
+[Fri Jul 31 01:04:32 2026] 127.0.0.1:52958 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:04:32 2026] 127.0.0.1:52958 Closing
+[Fri Jul 31 01:05:31 2026] 127.0.0.1:45182 Accepted
+[Fri Jul 31 01:05:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:05:32 2026] 127.0.0.1:45182 [200]: GET /api/stats.php
+[Fri Jul 31 01:05:32 2026] 127.0.0.1:45182 Closing
+[Fri Jul 31 01:05:32 2026] 127.0.0.1:45192 Accepted
+[Fri Jul 31 01:05:32 2026] 127.0.0.1:45192 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:05:32 2026] 127.0.0.1:45192 Closing
+[Fri Jul 31 01:06:31 2026] 127.0.0.1:56088 Accepted
+[Fri Jul 31 01:06:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:06:32 2026] 127.0.0.1:56088 [200]: GET /api/stats.php
+[Fri Jul 31 01:06:32 2026] 127.0.0.1:56088 Closing
+[Fri Jul 31 01:06:32 2026] 127.0.0.1:56092 Accepted
+[Fri Jul 31 01:06:32 2026] 127.0.0.1:56092 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:06:32 2026] 127.0.0.1:56092 Closing
+[Fri Jul 31 01:07:31 2026] 127.0.0.1:35864 Accepted
+[Fri Jul 31 01:07:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:07:32 2026] 127.0.0.1:35864 [200]: GET /api/stats.php
+[Fri Jul 31 01:07:32 2026] 127.0.0.1:35864 Closing
+[Fri Jul 31 01:07:32 2026] 127.0.0.1:35866 Accepted
+[Fri Jul 31 01:07:32 2026] 127.0.0.1:35866 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:07:32 2026] 127.0.0.1:35866 Closing
+[Fri Jul 31 01:08:31 2026] 127.0.0.1:50450 Accepted
+[Fri Jul 31 01:08:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:08:32 2026] 127.0.0.1:50450 [200]: GET /api/stats.php
+[Fri Jul 31 01:08:32 2026] 127.0.0.1:50450 Closing
+[Fri Jul 31 01:08:32 2026] 127.0.0.1:50464 Accepted
+[Fri Jul 31 01:08:32 2026] 127.0.0.1:50464 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:08:32 2026] 127.0.0.1:50464 Closing
+[Fri Jul 31 01:09:31 2026] 127.0.0.1:43680 Accepted
+[Fri Jul 31 01:09:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:09:32 2026] 127.0.0.1:43680 [200]: GET /api/stats.php
+[Fri Jul 31 01:09:32 2026] 127.0.0.1:43680 Closing
+[Fri Jul 31 01:09:32 2026] 127.0.0.1:43682 Accepted
+[Fri Jul 31 01:09:32 2026] 127.0.0.1:43682 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:09:32 2026] 127.0.0.1:43682 Closing
+[Fri Jul 31 01:10:31 2026] 127.0.0.1:34048 Accepted
+[Fri Jul 31 01:10:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:10:32 2026] 127.0.0.1:34048 [200]: GET /api/stats.php
+[Fri Jul 31 01:10:32 2026] 127.0.0.1:34048 Closing
+[Fri Jul 31 01:10:32 2026] 127.0.0.1:34054 Accepted
+[Fri Jul 31 01:10:32 2026] 127.0.0.1:34054 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:10:32 2026] 127.0.0.1:34054 Closing
+[Fri Jul 31 01:11:31 2026] 127.0.0.1:53610 Accepted
+[Fri Jul 31 01:11:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:11:32 2026] 127.0.0.1:53610 [200]: GET /api/stats.php
+[Fri Jul 31 01:11:32 2026] 127.0.0.1:53610 Closing
+[Fri Jul 31 01:11:32 2026] 127.0.0.1:53620 Accepted
+[Fri Jul 31 01:11:32 2026] 127.0.0.1:53620 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:11:32 2026] 127.0.0.1:53620 Closing
+[Fri Jul 31 01:12:31 2026] 127.0.0.1:48400 Accepted
+[Fri Jul 31 01:12:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:12:32 2026] 127.0.0.1:48400 [200]: GET /api/stats.php
+[Fri Jul 31 01:12:32 2026] 127.0.0.1:48400 Closing
+[Fri Jul 31 01:12:32 2026] 127.0.0.1:48410 Accepted
+[Fri Jul 31 01:12:32 2026] 127.0.0.1:48410 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:12:32 2026] 127.0.0.1:48410 Closing
+[Fri Jul 31 01:13:31 2026] 127.0.0.1:33936 Accepted
+[Fri Jul 31 01:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:13:32 2026] 127.0.0.1:33936 [200]: GET /api/stats.php
+[Fri Jul 31 01:13:32 2026] 127.0.0.1:33936 Closing
+[Fri Jul 31 01:13:32 2026] 127.0.0.1:33946 Accepted
+[Fri Jul 31 01:13:32 2026] 127.0.0.1:33946 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:13:32 2026] 127.0.0.1:33946 Closing
+[Fri Jul 31 01:14:31 2026] 127.0.0.1:55404 Accepted
+[Fri Jul 31 01:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:14:32 2026] 127.0.0.1:55404 [200]: GET /api/stats.php
+[Fri Jul 31 01:14:32 2026] 127.0.0.1:55404 Closing
+[Fri Jul 31 01:14:32 2026] 127.0.0.1:55412 Accepted
+[Fri Jul 31 01:14:32 2026] 127.0.0.1:55412 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:14:32 2026] 127.0.0.1:55412 Closing
+[Fri Jul 31 01:15:31 2026] 127.0.0.1:59070 Accepted
+[Fri Jul 31 01:15:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:15:32 2026] 127.0.0.1:59070 [200]: GET /api/stats.php
+[Fri Jul 31 01:15:32 2026] 127.0.0.1:59070 Closing
+[Fri Jul 31 01:15:32 2026] 127.0.0.1:59080 Accepted
+[Fri Jul 31 01:15:32 2026] 127.0.0.1:59080 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:15:32 2026] 127.0.0.1:59080 Closing
+[Fri Jul 31 01:16:31 2026] 127.0.0.1:35222 Accepted
+[Fri Jul 31 01:16:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:16:32 2026] 127.0.0.1:35222 [200]: GET /api/stats.php
+[Fri Jul 31 01:16:32 2026] 127.0.0.1:35222 Closing
+[Fri Jul 31 01:16:32 2026] 127.0.0.1:35232 Accepted
+[Fri Jul 31 01:16:32 2026] 127.0.0.1:35232 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:16:32 2026] 127.0.0.1:35232 Closing
+[Fri Jul 31 01:17:31 2026] 127.0.0.1:38208 Accepted
+[Fri Jul 31 01:17:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:17:32 2026] 127.0.0.1:38208 [200]: GET /api/stats.php
+[Fri Jul 31 01:17:32 2026] 127.0.0.1:38208 Closing
+[Fri Jul 31 01:17:32 2026] 127.0.0.1:38224 Accepted
+[Fri Jul 31 01:17:32 2026] 127.0.0.1:38224 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:17:32 2026] 127.0.0.1:38224 Closing
+[Fri Jul 31 01:18:31 2026] 127.0.0.1:34158 Accepted
+[Fri Jul 31 01:18:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:18:32 2026] 127.0.0.1:34158 [200]: GET /api/stats.php
+[Fri Jul 31 01:18:32 2026] 127.0.0.1:34158 Closing
+[Fri Jul 31 01:18:32 2026] 127.0.0.1:34162 Accepted
+[Fri Jul 31 01:18:32 2026] 127.0.0.1:34162 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:18:32 2026] 127.0.0.1:34162 Closing
+[Fri Jul 31 01:19:31 2026] 127.0.0.1:52842 Accepted
+[Fri Jul 31 01:19:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:19:32 2026] 127.0.0.1:52842 [200]: GET /api/stats.php
+[Fri Jul 31 01:19:32 2026] 127.0.0.1:52842 Closing
+[Fri Jul 31 01:19:32 2026] 127.0.0.1:52852 Accepted
+[Fri Jul 31 01:19:32 2026] 127.0.0.1:52852 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:19:32 2026] 127.0.0.1:52852 Closing
+[Fri Jul 31 01:20:31 2026] 127.0.0.1:46862 Accepted
+[Fri Jul 31 01:20:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:20:32 2026] 127.0.0.1:46862 [200]: GET /api/stats.php
+[Fri Jul 31 01:20:32 2026] 127.0.0.1:46862 Closing
+[Fri Jul 31 01:20:32 2026] 127.0.0.1:46876 Accepted
+[Fri Jul 31 01:20:32 2026] 127.0.0.1:46876 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:20:32 2026] 127.0.0.1:46876 Closing
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35578 Accepted
+[Fri Jul 31 01:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35578 [200]: GET /api/stats.php
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35578 Closing
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35580 Accepted
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35580 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:21:31 2026] 127.0.0.1:35580 Closing
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45950 Accepted
+[Fri Jul 31 01:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45950 [200]: GET /api/stats.php
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45950 Closing
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45960 Accepted
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45960 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:22:31 2026] 127.0.0.1:45960 Closing
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50424 Accepted
+[Fri Jul 31 01:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50424 [200]: GET /api/stats.php
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50424 Closing
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50426 Accepted
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50426 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:23:31 2026] 127.0.0.1:50426 Closing
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54746 Accepted
+[Fri Jul 31 01:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54746 [200]: GET /api/stats.php
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54746 Closing
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54752 Accepted
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54752 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:24:31 2026] 127.0.0.1:54752 Closing
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41076 Accepted
+[Fri Jul 31 01:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41076 [200]: GET /api/stats.php
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41076 Closing
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41080 Accepted
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41080 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:25:31 2026] 127.0.0.1:41080 Closing
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33462 Accepted
+[Fri Jul 31 01:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33462 [200]: GET /api/stats.php
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33462 Closing
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33474 Accepted
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33474 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:26:31 2026] 127.0.0.1:33474 Closing
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51644 Accepted
+[Fri Jul 31 01:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51644 [200]: GET /api/stats.php
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51644 Closing
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51650 Accepted
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51650 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:27:31 2026] 127.0.0.1:51650 Closing
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33274 Accepted
+[Fri Jul 31 01:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33274 [200]: GET /api/stats.php
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33274 Closing
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33290 Accepted
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33290 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:28:31 2026] 127.0.0.1:33290 Closing
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56506 Accepted
+[Fri Jul 31 01:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56506 [200]: GET /api/stats.php
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56506 Closing
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56510 Accepted
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56510 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:29:31 2026] 127.0.0.1:56510 Closing
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50848 Accepted
+[Fri Jul 31 01:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50848 [200]: GET /api/stats.php
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50848 Closing
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50862 Accepted
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50862 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:30:31 2026] 127.0.0.1:50862 Closing
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54676 Accepted
+[Fri Jul 31 01:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54676 [200]: GET /api/stats.php
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54676 Closing
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54692 Accepted
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54692 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:31:31 2026] 127.0.0.1:54692 Closing
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56790 Accepted
+[Fri Jul 31 01:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56790 [200]: GET /api/stats.php
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56790 Closing
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56804 Accepted
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56804 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:32:31 2026] 127.0.0.1:56804 Closing
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48624 Accepted
+[Fri Jul 31 01:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48624 [200]: GET /api/stats.php
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48624 Closing
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48628 Accepted
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48628 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:33:31 2026] 127.0.0.1:48628 Closing
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51196 Accepted
+[Fri Jul 31 01:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51196 [200]: GET /api/stats.php
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51196 Closing
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51208 Accepted
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51208 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:34:31 2026] 127.0.0.1:51208 Closing
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34354 Accepted
+[Fri Jul 31 01:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34354 [200]: GET /api/stats.php
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34354 Closing
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34358 Accepted
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34358 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:35:31 2026] 127.0.0.1:34358 Closing
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34116 Accepted
+[Fri Jul 31 01:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34116 [200]: GET /api/stats.php
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34116 Closing
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34122 Accepted
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34122 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:36:31 2026] 127.0.0.1:34122 Closing
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49972 Accepted
+[Fri Jul 31 01:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49972 [200]: GET /api/stats.php
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49972 Closing
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49974 Accepted
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49974 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:37:31 2026] 127.0.0.1:49974 Closing
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59262 Accepted
+[Fri Jul 31 01:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59262 [200]: GET /api/stats.php
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59262 Closing
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59266 Accepted
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59266 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:38:31 2026] 127.0.0.1:59266 Closing
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47426 Accepted
+[Fri Jul 31 01:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47426 [200]: GET /api/stats.php
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47426 Closing
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47430 Accepted
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47430 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:39:31 2026] 127.0.0.1:47430 Closing
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32984 Accepted
+[Fri Jul 31 01:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32984 [200]: GET /api/stats.php
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32984 Closing
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32990 Accepted
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32990 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:40:31 2026] 127.0.0.1:32990 Closing
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60410 Accepted
+[Fri Jul 31 01:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60410 [200]: GET /api/stats.php
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60410 Closing
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60412 Accepted
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60412 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:41:31 2026] 127.0.0.1:60412 Closing
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33286 Accepted
+[Fri Jul 31 01:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33286 [200]: GET /api/stats.php
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33286 Closing
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33288 Accepted
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33288 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:42:31 2026] 127.0.0.1:33288 Closing
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34122 Accepted
+[Fri Jul 31 01:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34122 [200]: GET /api/stats.php
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34122 Closing
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34136 Accepted
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34136 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:43:31 2026] 127.0.0.1:34136 Closing
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42598 Accepted
+[Fri Jul 31 01:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42598 [200]: GET /api/stats.php
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42598 Closing
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42610 Accepted
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42610 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:44:31 2026] 127.0.0.1:42610 Closing
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56694 Accepted
+[Fri Jul 31 01:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56694 [200]: GET /api/stats.php
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56694 Closing
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56702 Accepted
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56702 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:45:31 2026] 127.0.0.1:56702 Closing
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52910 Accepted
+[Fri Jul 31 01:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52910 [200]: GET /api/stats.php
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52910 Closing
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52924 Accepted
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52924 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:46:31 2026] 127.0.0.1:52924 Closing
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40288 Accepted
+[Fri Jul 31 01:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40288 [200]: GET /api/stats.php
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40288 Closing
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40300 Accepted
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40300 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:47:31 2026] 127.0.0.1:40300 Closing
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53694 Accepted
+[Fri Jul 31 01:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53694 [200]: GET /api/stats.php
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53694 Closing
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53698 Accepted
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53698 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:48:31 2026] 127.0.0.1:53698 Closing
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54366 Accepted
+[Fri Jul 31 01:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54366 [200]: GET /api/stats.php
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54366 Closing
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54382 Accepted
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54382 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:49:31 2026] 127.0.0.1:54382 Closing
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37122 Accepted
+[Fri Jul 31 01:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37122 [200]: GET /api/stats.php
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37122 Closing
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37130 Accepted
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37130 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:50:31 2026] 127.0.0.1:37130 Closing
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47438 Accepted
+[Fri Jul 31 01:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47438 [200]: GET /api/stats.php
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47438 Closing
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47452 Accepted
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47452 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:51:31 2026] 127.0.0.1:47452 Closing
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49452 Accepted
+[Fri Jul 31 01:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49452 [200]: GET /api/stats.php
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49452 Closing
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49468 Accepted
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49468 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:52:31 2026] 127.0.0.1:49468 Closing
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39262 Accepted
+[Fri Jul 31 01:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39262 [200]: GET /api/stats.php
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39262 Closing
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39266 Accepted
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39266 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:53:31 2026] 127.0.0.1:39266 Closing
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38912 Accepted
+[Fri Jul 31 01:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38912 [200]: GET /api/stats.php
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38912 Closing
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38918 Accepted
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38918 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:54:31 2026] 127.0.0.1:38918 Closing
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40540 Accepted
+[Fri Jul 31 01:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40540 [200]: GET /api/stats.php
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40540 Closing
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40548 Accepted
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40548 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:55:31 2026] 127.0.0.1:40548 Closing
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43520 Accepted
+[Fri Jul 31 01:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43520 [200]: GET /api/stats.php
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43520 Closing
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43532 Accepted
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43532 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:56:31 2026] 127.0.0.1:43532 Closing
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59598 Accepted
+[Fri Jul 31 01:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59598 [200]: GET /api/stats.php
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59598 Closing
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59610 Accepted
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59610 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:57:31 2026] 127.0.0.1:59610 Closing
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59636 Accepted
+[Fri Jul 31 01:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59636 [200]: GET /api/stats.php
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59636 Closing
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59640 Accepted
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59640 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:58:31 2026] 127.0.0.1:59640 Closing
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41892 Accepted
+[Fri Jul 31 01:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41892 [200]: GET /api/stats.php
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41892 Closing
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41902 Accepted
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41902 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 01:59:31 2026] 127.0.0.1:41902 Closing
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50492 Accepted
+[Fri Jul 31 02:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50492 [200]: GET /api/stats.php
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50492 Closing
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50502 Accepted
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50502 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:00:31 2026] 127.0.0.1:50502 Closing
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45456 Accepted
+[Fri Jul 31 02:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45456 [200]: GET /api/stats.php
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45456 Closing
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45472 Accepted
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45472 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:01:31 2026] 127.0.0.1:45472 Closing
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51222 Accepted
+[Fri Jul 31 02:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51222 [200]: GET /api/stats.php
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51222 Closing
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51236 Accepted
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51236 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:02:31 2026] 127.0.0.1:51236 Closing
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51180 Accepted
+[Fri Jul 31 02:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51180 [200]: GET /api/stats.php
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51180 Closing
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51196 Accepted
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51196 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:03:31 2026] 127.0.0.1:51196 Closing
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50280 Accepted
+[Fri Jul 31 02:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50280 [200]: GET /api/stats.php
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50280 Closing
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50284 Accepted
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50284 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:04:31 2026] 127.0.0.1:50284 Closing
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36058 Accepted
+[Fri Jul 31 02:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36058 [200]: GET /api/stats.php
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36058 Closing
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36068 Accepted
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36068 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:05:31 2026] 127.0.0.1:36068 Closing
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47942 Accepted
+[Fri Jul 31 02:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47942 [200]: GET /api/stats.php
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47942 Closing
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47954 Accepted
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47954 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:06:31 2026] 127.0.0.1:47954 Closing
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34420 Accepted
+[Fri Jul 31 02:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34420 [200]: GET /api/stats.php
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34420 Closing
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34430 Accepted
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34430 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:07:31 2026] 127.0.0.1:34430 Closing
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54650 Accepted
+[Fri Jul 31 02:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54650 [200]: GET /api/stats.php
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54650 Closing
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54658 Accepted
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54658 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:08:31 2026] 127.0.0.1:54658 Closing
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52146 Accepted
+[Fri Jul 31 02:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52146 [200]: GET /api/stats.php
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52146 Closing
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52148 Accepted
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52148 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:09:31 2026] 127.0.0.1:52148 Closing
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48252 Accepted
+[Fri Jul 31 02:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48252 [200]: GET /api/stats.php
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48252 Closing
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48256 Accepted
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48256 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:10:31 2026] 127.0.0.1:48256 Closing
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58528 Accepted
+[Fri Jul 31 02:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58528 [200]: GET /api/stats.php
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58528 Closing
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58534 Accepted
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58534 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:11:31 2026] 127.0.0.1:58534 Closing
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49790 Accepted
+[Fri Jul 31 02:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49790 [200]: GET /api/stats.php
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49790 Closing
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49792 Accepted
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49792 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:12:31 2026] 127.0.0.1:49792 Closing
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47604 Accepted
+[Fri Jul 31 02:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47604 [200]: GET /api/stats.php
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47604 Closing
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47614 Accepted
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47614 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:13:31 2026] 127.0.0.1:47614 Closing
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58600 Accepted
+[Fri Jul 31 02:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58600 [200]: GET /api/stats.php
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58600 Closing
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58614 Accepted
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58614 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:14:31 2026] 127.0.0.1:58614 Closing
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57886 Accepted
+[Fri Jul 31 02:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57886 [200]: GET /api/stats.php
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57886 Closing
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57900 Accepted
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57900 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:15:31 2026] 127.0.0.1:57900 Closing
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52710 Accepted
+[Fri Jul 31 02:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52710 [200]: GET /api/stats.php
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52710 Closing
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52718 Accepted
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52718 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:16:31 2026] 127.0.0.1:52718 Closing
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52708 Accepted
+[Fri Jul 31 02:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52708 [200]: GET /api/stats.php
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52708 Closing
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52716 Accepted
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52716 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:17:31 2026] 127.0.0.1:52716 Closing
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37162 Accepted
+[Fri Jul 31 02:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37162 [200]: GET /api/stats.php
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37162 Closing
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37174 Accepted
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37174 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:18:31 2026] 127.0.0.1:37174 Closing
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49340 Accepted
+[Fri Jul 31 02:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49340 [200]: GET /api/stats.php
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49340 Closing
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49356 Accepted
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49356 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:19:31 2026] 127.0.0.1:49356 Closing
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58370 Accepted
+[Fri Jul 31 02:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58370 [200]: GET /api/stats.php
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58370 Closing
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58374 Accepted
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58374 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:20:31 2026] 127.0.0.1:58374 Closing
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41878 Accepted
+[Fri Jul 31 02:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41878 [200]: GET /api/stats.php
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41878 Closing
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41890 Accepted
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41890 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:21:31 2026] 127.0.0.1:41890 Closing
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57602 Accepted
+[Fri Jul 31 02:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57602 [200]: GET /api/stats.php
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57602 Closing
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57614 Accepted
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57614 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:22:31 2026] 127.0.0.1:57614 Closing
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35872 Accepted
+[Fri Jul 31 02:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35872 [200]: GET /api/stats.php
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35872 Closing
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35878 Accepted
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35878 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:23:31 2026] 127.0.0.1:35878 Closing
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42426 Accepted
+[Fri Jul 31 02:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42426 [200]: GET /api/stats.php
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42426 Closing
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42428 Accepted
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42428 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:24:31 2026] 127.0.0.1:42428 Closing
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56330 Accepted
+[Fri Jul 31 02:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56330 [200]: GET /api/stats.php
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56330 Closing
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56344 Accepted
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56344 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:25:31 2026] 127.0.0.1:56344 Closing
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35906 Accepted
+[Fri Jul 31 02:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35906 [200]: GET /api/stats.php
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35906 Closing
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35912 Accepted
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35912 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:26:31 2026] 127.0.0.1:35912 Closing
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51838 Accepted
+[Fri Jul 31 02:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51838 [200]: GET /api/stats.php
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51838 Closing
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51854 Accepted
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51854 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:27:31 2026] 127.0.0.1:51854 Closing
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48418 Accepted
+[Fri Jul 31 02:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48418 [200]: GET /api/stats.php
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48418 Closing
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48434 Accepted
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48434 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:28:31 2026] 127.0.0.1:48434 Closing
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52510 Accepted
+[Fri Jul 31 02:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52510 [200]: GET /api/stats.php
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52510 Closing
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52516 Accepted
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52516 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:29:31 2026] 127.0.0.1:52516 Closing
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56460 Accepted
+[Fri Jul 31 02:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56460 [200]: GET /api/stats.php
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56460 Closing
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56474 Accepted
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56474 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:30:31 2026] 127.0.0.1:56474 Closing
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52182 Accepted
+[Fri Jul 31 02:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52182 [200]: GET /api/stats.php
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52182 Closing
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52194 Accepted
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52194 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:31:31 2026] 127.0.0.1:52194 Closing
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44210 Accepted
+[Fri Jul 31 02:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44210 [200]: GET /api/stats.php
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44210 Closing
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44212 Accepted
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44212 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:32:31 2026] 127.0.0.1:44212 Closing
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59956 Accepted
+[Fri Jul 31 02:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59956 [200]: GET /api/stats.php
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59956 Closing
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59968 Accepted
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59968 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:33:31 2026] 127.0.0.1:59968 Closing
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38538 Accepted
+[Fri Jul 31 02:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38538 [200]: GET /api/stats.php
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38538 Closing
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38546 Accepted
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38546 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:34:31 2026] 127.0.0.1:38546 Closing
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41908 Accepted
+[Fri Jul 31 02:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41908 [200]: GET /api/stats.php
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41908 Closing
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41914 Accepted
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41914 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:35:31 2026] 127.0.0.1:41914 Closing
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56260 Accepted
+[Fri Jul 31 02:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56260 [200]: GET /api/stats.php
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56260 Closing
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56264 Accepted
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56264 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:36:31 2026] 127.0.0.1:56264 Closing
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41914 Accepted
+[Fri Jul 31 02:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41914 [200]: GET /api/stats.php
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41914 Closing
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41922 Accepted
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41922 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:37:31 2026] 127.0.0.1:41922 Closing
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55298 Accepted
+[Fri Jul 31 02:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55298 [200]: GET /api/stats.php
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55298 Closing
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55302 Accepted
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55302 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:38:31 2026] 127.0.0.1:55302 Closing
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55794 Accepted
+[Fri Jul 31 02:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55794 [200]: GET /api/stats.php
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55794 Closing
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55796 Accepted
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55796 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:39:31 2026] 127.0.0.1:55796 Closing
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36010 Accepted
+[Fri Jul 31 02:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36010 [200]: GET /api/stats.php
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36010 Closing
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36020 Accepted
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36020 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:40:31 2026] 127.0.0.1:36020 Closing
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53728 Accepted
+[Fri Jul 31 02:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53728 [200]: GET /api/stats.php
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53728 Closing
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53730 Accepted
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53730 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:41:31 2026] 127.0.0.1:53730 Closing
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49882 Accepted
+[Fri Jul 31 02:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49882 [200]: GET /api/stats.php
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49882 Closing
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49892 Accepted
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49892 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:42:31 2026] 127.0.0.1:49892 Closing
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58240 Accepted
+[Fri Jul 31 02:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58240 [200]: GET /api/stats.php
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58240 Closing
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58248 Accepted
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58248 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:43:31 2026] 127.0.0.1:58248 Closing
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45310 Accepted
+[Fri Jul 31 02:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45310 [200]: GET /api/stats.php
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45310 Closing
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45316 Accepted
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45316 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:44:31 2026] 127.0.0.1:45316 Closing
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40952 Accepted
+[Fri Jul 31 02:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40952 [200]: GET /api/stats.php
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40952 Closing
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40960 Accepted
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40960 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:45:31 2026] 127.0.0.1:40960 Closing
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40476 Accepted
+[Fri Jul 31 02:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40476 [200]: GET /api/stats.php
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40476 Closing
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40482 Accepted
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40482 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:46:31 2026] 127.0.0.1:40482 Closing
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52516 Accepted
+[Fri Jul 31 02:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52516 [200]: GET /api/stats.php
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52516 Closing
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52518 Accepted
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52518 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:47:31 2026] 127.0.0.1:52518 Closing
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45498 Accepted
+[Fri Jul 31 02:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45498 [200]: GET /api/stats.php
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45498 Closing
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45512 Accepted
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45512 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:48:31 2026] 127.0.0.1:45512 Closing
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57400 Accepted
+[Fri Jul 31 02:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57400 [200]: GET /api/stats.php
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57400 Closing
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57404 Accepted
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57404 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:49:31 2026] 127.0.0.1:57404 Closing
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56468 Accepted
+[Fri Jul 31 02:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56468 [200]: GET /api/stats.php
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56468 Closing
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56480 Accepted
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56480 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:50:31 2026] 127.0.0.1:56480 Closing
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43692 Accepted
+[Fri Jul 31 02:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43692 [200]: GET /api/stats.php
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43692 Closing
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43694 Accepted
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43694 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:51:31 2026] 127.0.0.1:43694 Closing
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49580 Accepted
+[Fri Jul 31 02:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49580 [200]: GET /api/stats.php
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49580 Closing
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49584 Accepted
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49584 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:52:31 2026] 127.0.0.1:49584 Closing
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36696 Accepted
+[Fri Jul 31 02:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36696 [200]: GET /api/stats.php
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36696 Closing
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36712 Accepted
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36712 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:53:31 2026] 127.0.0.1:36712 Closing
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35132 Accepted
+[Fri Jul 31 02:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35132 [200]: GET /api/stats.php
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35132 Closing
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35144 Accepted
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35144 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:54:31 2026] 127.0.0.1:35144 Closing
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59834 Accepted
+[Fri Jul 31 02:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59834 [200]: GET /api/stats.php
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59834 Closing
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59846 Accepted
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59846 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:55:31 2026] 127.0.0.1:59846 Closing
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51600 Accepted
+[Fri Jul 31 02:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51600 [200]: GET /api/stats.php
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51600 Closing
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51606 Accepted
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51606 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:56:31 2026] 127.0.0.1:51606 Closing
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56954 Accepted
+[Fri Jul 31 02:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56954 [200]: GET /api/stats.php
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56954 Closing
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56960 Accepted
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56960 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:57:31 2026] 127.0.0.1:56960 Closing
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57296 Accepted
+[Fri Jul 31 02:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57296 [200]: GET /api/stats.php
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57296 Closing
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57308 Accepted
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57308 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:58:31 2026] 127.0.0.1:57308 Closing
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56566 Accepted
+[Fri Jul 31 02:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56566 [200]: GET /api/stats.php
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56566 Closing
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56572 Accepted
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56572 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 02:59:31 2026] 127.0.0.1:56572 Closing
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39188 Accepted
+[Fri Jul 31 03:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39188 [200]: GET /api/stats.php
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39188 Closing
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39194 Accepted
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39194 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:00:31 2026] 127.0.0.1:39194 Closing
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43486 Accepted
+[Fri Jul 31 03:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43486 [200]: GET /api/stats.php
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43486 Closing
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43500 Accepted
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43500 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:01:31 2026] 127.0.0.1:43500 Closing
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40384 Accepted
+[Fri Jul 31 03:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40384 [200]: GET /api/stats.php
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40384 Closing
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40386 Accepted
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40386 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:02:31 2026] 127.0.0.1:40386 Closing
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48122 Accepted
+[Fri Jul 31 03:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48122 [200]: GET /api/stats.php
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48122 Closing
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48126 Accepted
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48126 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:03:31 2026] 127.0.0.1:48126 Closing
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49732 Accepted
+[Fri Jul 31 03:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49732 [200]: GET /api/stats.php
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49732 Closing
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49744 Accepted
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49744 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:04:31 2026] 127.0.0.1:49744 Closing
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50742 Accepted
+[Fri Jul 31 03:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50742 [200]: GET /api/stats.php
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50742 Closing
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50754 Accepted
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50754 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:05:31 2026] 127.0.0.1:50754 Closing
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60982 Accepted
+[Fri Jul 31 03:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60982 [200]: GET /api/stats.php
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60982 Closing
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60996 Accepted
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60996 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:06:31 2026] 127.0.0.1:60996 Closing
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37616 Accepted
+[Fri Jul 31 03:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37616 [200]: GET /api/stats.php
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37616 Closing
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37624 Accepted
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37624 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:07:31 2026] 127.0.0.1:37624 Closing
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39602 Accepted
+[Fri Jul 31 03:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39602 [200]: GET /api/stats.php
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39602 Closing
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39608 Accepted
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39608 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:08:31 2026] 127.0.0.1:39608 Closing
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43756 Accepted
+[Fri Jul 31 03:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43756 [200]: GET /api/stats.php
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43756 Closing
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43760 Accepted
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43760 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:09:31 2026] 127.0.0.1:43760 Closing
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37472 Accepted
+[Fri Jul 31 03:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37472 [200]: GET /api/stats.php
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37472 Closing
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37474 Accepted
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37474 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:10:31 2026] 127.0.0.1:37474 Closing
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39662 Accepted
+[Fri Jul 31 03:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39662 [200]: GET /api/stats.php
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39662 Closing
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39672 Accepted
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39672 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:11:31 2026] 127.0.0.1:39672 Closing
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37042 Accepted
+[Fri Jul 31 03:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37042 [200]: GET /api/stats.php
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37042 Closing
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37050 Accepted
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37050 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:12:31 2026] 127.0.0.1:37050 Closing
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:58990 Accepted
+[Fri Jul 31 03:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:58990 [200]: GET /api/stats.php
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:58990 Closing
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:59000 Accepted
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:59000 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:13:31 2026] 127.0.0.1:59000 Closing
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49398 Accepted
+[Fri Jul 31 03:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49398 [200]: GET /api/stats.php
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49398 Closing
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49412 Accepted
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49412 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:14:31 2026] 127.0.0.1:49412 Closing
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56128 Accepted
+[Fri Jul 31 03:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56128 [200]: GET /api/stats.php
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56128 Closing
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56136 Accepted
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56136 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:15:31 2026] 127.0.0.1:56136 Closing
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58702 Accepted
+[Fri Jul 31 03:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58702 [200]: GET /api/stats.php
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58702 Closing
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58704 Accepted
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58704 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:16:31 2026] 127.0.0.1:58704 Closing
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54426 Accepted
+[Fri Jul 31 03:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54426 [200]: GET /api/stats.php
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54426 Closing
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54430 Accepted
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54430 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:17:31 2026] 127.0.0.1:54430 Closing
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40438 Accepted
+[Fri Jul 31 03:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40438 [200]: GET /api/stats.php
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40438 Closing
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40448 Accepted
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40448 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:18:31 2026] 127.0.0.1:40448 Closing
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57758 Accepted
+[Fri Jul 31 03:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57758 [200]: GET /api/stats.php
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57758 Closing
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57762 Accepted
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57762 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:19:31 2026] 127.0.0.1:57762 Closing
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52454 Accepted
+[Fri Jul 31 03:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52454 [200]: GET /api/stats.php
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52454 Closing
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52456 Accepted
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52456 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:20:31 2026] 127.0.0.1:52456 Closing
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48364 Accepted
+[Fri Jul 31 03:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48364 [200]: GET /api/stats.php
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48364 Closing
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48374 Accepted
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48374 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:21:31 2026] 127.0.0.1:48374 Closing
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55302 Accepted
+[Fri Jul 31 03:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55302 [200]: GET /api/stats.php
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55302 Closing
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55314 Accepted
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55314 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:22:31 2026] 127.0.0.1:55314 Closing
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60118 Accepted
+[Fri Jul 31 03:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60118 [200]: GET /api/stats.php
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60118 Closing
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60122 Accepted
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60122 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:23:31 2026] 127.0.0.1:60122 Closing
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39594 Accepted
+[Fri Jul 31 03:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39594 [200]: GET /api/stats.php
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39594 Closing
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39610 Accepted
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39610 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:24:31 2026] 127.0.0.1:39610 Closing
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47080 Accepted
+[Fri Jul 31 03:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47080 [200]: GET /api/stats.php
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47080 Closing
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47084 Accepted
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47084 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:25:31 2026] 127.0.0.1:47084 Closing
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57246 Accepted
+[Fri Jul 31 03:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57246 [200]: GET /api/stats.php
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57246 Closing
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57262 Accepted
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57262 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:26:31 2026] 127.0.0.1:57262 Closing
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48334 Accepted
+[Fri Jul 31 03:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48334 [200]: GET /api/stats.php
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48334 Closing
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48344 Accepted
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48344 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:27:31 2026] 127.0.0.1:48344 Closing
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32780 Accepted
+[Fri Jul 31 03:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32780 [200]: GET /api/stats.php
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32780 Closing
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32792 Accepted
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32792 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:28:31 2026] 127.0.0.1:32792 Closing
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60498 Accepted
+[Fri Jul 31 03:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60498 [200]: GET /api/stats.php
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60498 Closing
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60512 Accepted
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60512 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:29:31 2026] 127.0.0.1:60512 Closing
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52678 Accepted
+[Fri Jul 31 03:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52678 [200]: GET /api/stats.php
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52678 Closing
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52694 Accepted
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52694 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:30:31 2026] 127.0.0.1:52694 Closing
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34548 Accepted
+[Fri Jul 31 03:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34548 [200]: GET /api/stats.php
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34548 Closing
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34562 Accepted
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34562 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:31:31 2026] 127.0.0.1:34562 Closing
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34652 Accepted
+[Fri Jul 31 03:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34652 [200]: GET /api/stats.php
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34652 Closing
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34662 Accepted
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34662 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:32:31 2026] 127.0.0.1:34662 Closing
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55656 Accepted
+[Fri Jul 31 03:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55656 [200]: GET /api/stats.php
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55656 Closing
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55670 Accepted
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55670 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:33:31 2026] 127.0.0.1:55670 Closing
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46976 Accepted
+[Fri Jul 31 03:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46976 [200]: GET /api/stats.php
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46976 Closing
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46992 Accepted
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46992 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:34:31 2026] 127.0.0.1:46992 Closing
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58954 Accepted
+[Fri Jul 31 03:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58954 [200]: GET /api/stats.php
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58954 Closing
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58958 Accepted
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58958 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:35:31 2026] 127.0.0.1:58958 Closing
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42204 Accepted
+[Fri Jul 31 03:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42204 [200]: GET /api/stats.php
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42204 Closing
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42210 Accepted
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42210 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:36:31 2026] 127.0.0.1:42210 Closing
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41366 Accepted
+[Fri Jul 31 03:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41366 [200]: GET /api/stats.php
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41366 Closing
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41378 Accepted
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41378 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:37:31 2026] 127.0.0.1:41378 Closing
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48386 Accepted
+[Fri Jul 31 03:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48386 [200]: GET /api/stats.php
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48386 Closing
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48390 Accepted
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48390 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:38:31 2026] 127.0.0.1:48390 Closing
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56478 Accepted
+[Fri Jul 31 03:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56478 [200]: GET /api/stats.php
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56478 Closing
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56482 Accepted
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56482 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:39:31 2026] 127.0.0.1:56482 Closing
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53834 Accepted
+[Fri Jul 31 03:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53834 [200]: GET /api/stats.php
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53834 Closing
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53846 Accepted
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53846 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:40:31 2026] 127.0.0.1:53846 Closing
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:40992 Accepted
+[Fri Jul 31 03:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:40992 [200]: GET /api/stats.php
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:40992 Closing
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:41008 Accepted
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:41008 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:41:31 2026] 127.0.0.1:41008 Closing
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39022 Accepted
+[Fri Jul 31 03:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39022 [200]: GET /api/stats.php
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39022 Closing
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39026 Accepted
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39026 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:42:31 2026] 127.0.0.1:39026 Closing
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55856 Accepted
+[Fri Jul 31 03:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55856 [200]: GET /api/stats.php
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55856 Closing
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55872 Accepted
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55872 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:43:31 2026] 127.0.0.1:55872 Closing
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44690 Accepted
+[Fri Jul 31 03:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44690 [200]: GET /api/stats.php
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44690 Closing
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44702 Accepted
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44702 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:44:31 2026] 127.0.0.1:44702 Closing
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46586 Accepted
+[Fri Jul 31 03:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46586 [200]: GET /api/stats.php
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46586 Closing
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46600 Accepted
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46600 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:45:31 2026] 127.0.0.1:46600 Closing
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44264 Accepted
+[Fri Jul 31 03:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44264 [200]: GET /api/stats.php
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44264 Closing
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44272 Accepted
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44272 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:46:31 2026] 127.0.0.1:44272 Closing
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44750 Accepted
+[Fri Jul 31 03:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44750 [200]: GET /api/stats.php
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44750 Closing
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44764 Accepted
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44764 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:47:31 2026] 127.0.0.1:44764 Closing
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35108 Accepted
+[Fri Jul 31 03:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35108 [200]: GET /api/stats.php
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35108 Closing
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35120 Accepted
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35120 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:48:31 2026] 127.0.0.1:35120 Closing
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53678 Accepted
+[Fri Jul 31 03:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53678 [200]: GET /api/stats.php
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53678 Closing
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53684 Accepted
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53684 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:49:31 2026] 127.0.0.1:53684 Closing
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55728 Accepted
+[Fri Jul 31 03:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55728 [200]: GET /api/stats.php
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55728 Closing
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55730 Accepted
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55730 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:50:31 2026] 127.0.0.1:55730 Closing
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50230 Accepted
+[Fri Jul 31 03:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50230 [200]: GET /api/stats.php
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50230 Closing
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50238 Accepted
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50238 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:51:31 2026] 127.0.0.1:50238 Closing
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43140 Accepted
+[Fri Jul 31 03:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43140 [200]: GET /api/stats.php
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43140 Closing
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43144 Accepted
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43144 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:52:31 2026] 127.0.0.1:43144 Closing
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32906 Accepted
+[Fri Jul 31 03:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32906 [200]: GET /api/stats.php
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32906 Closing
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32916 Accepted
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32916 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:53:31 2026] 127.0.0.1:32916 Closing
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36170 Accepted
+[Fri Jul 31 03:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36170 [200]: GET /api/stats.php
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36170 Closing
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36176 Accepted
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36176 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:54:31 2026] 127.0.0.1:36176 Closing
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50714 Accepted
+[Fri Jul 31 03:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50714 [200]: GET /api/stats.php
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50714 Closing
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50728 Accepted
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50728 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:55:31 2026] 127.0.0.1:50728 Closing
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60120 Accepted
+[Fri Jul 31 03:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60120 [200]: GET /api/stats.php
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60120 Closing
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60128 Accepted
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60128 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:56:31 2026] 127.0.0.1:60128 Closing
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56976 Accepted
+[Fri Jul 31 03:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56976 [200]: GET /api/stats.php
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56976 Closing
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56990 Accepted
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56990 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:57:31 2026] 127.0.0.1:56990 Closing
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50968 Accepted
+[Fri Jul 31 03:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50968 [200]: GET /api/stats.php
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50968 Closing
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50974 Accepted
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50974 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:58:31 2026] 127.0.0.1:50974 Closing
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44976 Accepted
+[Fri Jul 31 03:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44976 [200]: GET /api/stats.php
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44976 Closing
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44986 Accepted
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44986 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 03:59:31 2026] 127.0.0.1:44986 Closing
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51094 Accepted
+[Fri Jul 31 04:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51094 [200]: GET /api/stats.php
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51094 Closing
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51104 Accepted
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51104 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:00:31 2026] 127.0.0.1:51104 Closing
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44798 Accepted
+[Fri Jul 31 04:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44798 [200]: GET /api/stats.php
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44798 Closing
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44802 Accepted
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44802 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:01:31 2026] 127.0.0.1:44802 Closing
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60184 Accepted
+[Fri Jul 31 04:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60184 [200]: GET /api/stats.php
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60184 Closing
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60188 Accepted
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60188 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:02:31 2026] 127.0.0.1:60188 Closing
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58178 Accepted
+[Fri Jul 31 04:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58178 [200]: GET /api/stats.php
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58178 Closing
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58184 Accepted
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58184 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:03:31 2026] 127.0.0.1:58184 Closing
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45734 Accepted
+[Fri Jul 31 04:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45734 [200]: GET /api/stats.php
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45734 Closing
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45742 Accepted
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45742 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:04:31 2026] 127.0.0.1:45742 Closing
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55520 Accepted
+[Fri Jul 31 04:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55520 [200]: GET /api/stats.php
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55520 Closing
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55526 Accepted
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55526 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:05:31 2026] 127.0.0.1:55526 Closing
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53862 Accepted
+[Fri Jul 31 04:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53862 [200]: GET /api/stats.php
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53862 Closing
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53864 Accepted
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53864 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:06:31 2026] 127.0.0.1:53864 Closing
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37754 Accepted
+[Fri Jul 31 04:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37754 [200]: GET /api/stats.php
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37754 Closing
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37766 Accepted
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37766 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:07:31 2026] 127.0.0.1:37766 Closing
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41236 Accepted
+[Fri Jul 31 04:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41236 [200]: GET /api/stats.php
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41236 Closing
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41240 Accepted
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41240 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:08:31 2026] 127.0.0.1:41240 Closing
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39112 Accepted
+[Fri Jul 31 04:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39112 [200]: GET /api/stats.php
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39112 Closing
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39128 Accepted
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39128 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:09:31 2026] 127.0.0.1:39128 Closing
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38932 Accepted
+[Fri Jul 31 04:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38932 [200]: GET /api/stats.php
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38932 Closing
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38942 Accepted
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38942 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:10:31 2026] 127.0.0.1:38942 Closing
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45036 Accepted
+[Fri Jul 31 04:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45036 [200]: GET /api/stats.php
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45036 Closing
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45048 Accepted
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45048 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:11:31 2026] 127.0.0.1:45048 Closing
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56638 Accepted
+[Fri Jul 31 04:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56638 [200]: GET /api/stats.php
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56638 Closing
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56642 Accepted
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56642 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:12:31 2026] 127.0.0.1:56642 Closing
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52806 Accepted
+[Fri Jul 31 04:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52806 [200]: GET /api/stats.php
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52806 Closing
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52816 Accepted
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52816 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:13:31 2026] 127.0.0.1:52816 Closing
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42452 Accepted
+[Fri Jul 31 04:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42452 [200]: GET /api/stats.php
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42452 Closing
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42462 Accepted
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42462 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:14:31 2026] 127.0.0.1:42462 Closing
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35354 Accepted
+[Fri Jul 31 04:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35354 [200]: GET /api/stats.php
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35354 Closing
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35368 Accepted
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35368 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:15:31 2026] 127.0.0.1:35368 Closing
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56876 Accepted
+[Fri Jul 31 04:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56876 [200]: GET /api/stats.php
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56876 Closing
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56880 Accepted
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56880 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:16:31 2026] 127.0.0.1:56880 Closing
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34268 Accepted
+[Fri Jul 31 04:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34268 [200]: GET /api/stats.php
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34268 Closing
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34278 Accepted
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34278 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:17:31 2026] 127.0.0.1:34278 Closing
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57754 Accepted
+[Fri Jul 31 04:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57754 [200]: GET /api/stats.php
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57754 Closing
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57764 Accepted
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57764 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:18:31 2026] 127.0.0.1:57764 Closing
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33220 Accepted
+[Fri Jul 31 04:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33220 [200]: GET /api/stats.php
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33220 Closing
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33224 Accepted
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33224 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:19:31 2026] 127.0.0.1:33224 Closing
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33424 Accepted
+[Fri Jul 31 04:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33424 [200]: GET /api/stats.php
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33424 Closing
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33434 Accepted
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33434 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:20:31 2026] 127.0.0.1:33434 Closing
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55104 Accepted
+[Fri Jul 31 04:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55104 [200]: GET /api/stats.php
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55104 Closing
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55106 Accepted
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55106 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:21:31 2026] 127.0.0.1:55106 Closing
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35102 Accepted
+[Fri Jul 31 04:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35102 [200]: GET /api/stats.php
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35102 Closing
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35116 Accepted
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35116 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:22:31 2026] 127.0.0.1:35116 Closing
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48318 Accepted
+[Fri Jul 31 04:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48318 [200]: GET /api/stats.php
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48318 Closing
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48324 Accepted
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48324 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:23:31 2026] 127.0.0.1:48324 Closing
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59602 Accepted
+[Fri Jul 31 04:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59602 [200]: GET /api/stats.php
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59602 Closing
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59612 Accepted
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59612 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:24:31 2026] 127.0.0.1:59612 Closing
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57786 Accepted
+[Fri Jul 31 04:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57786 [200]: GET /api/stats.php
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57786 Closing
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57796 Accepted
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57796 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:25:31 2026] 127.0.0.1:57796 Closing
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59074 Accepted
+[Fri Jul 31 04:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59074 [200]: GET /api/stats.php
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59074 Closing
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59082 Accepted
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59082 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:26:31 2026] 127.0.0.1:59082 Closing
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32882 Accepted
+[Fri Jul 31 04:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32882 [200]: GET /api/stats.php
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32882 Closing
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32890 Accepted
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32890 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:27:31 2026] 127.0.0.1:32890 Closing
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58692 Accepted
+[Fri Jul 31 04:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58692 [200]: GET /api/stats.php
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58692 Closing
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58696 Accepted
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58696 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:28:31 2026] 127.0.0.1:58696 Closing
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49314 Accepted
+[Fri Jul 31 04:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49314 [200]: GET /api/stats.php
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49314 Closing
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49316 Accepted
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49316 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:29:31 2026] 127.0.0.1:49316 Closing
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55128 Accepted
+[Fri Jul 31 04:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55128 [200]: GET /api/stats.php
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55128 Closing
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55136 Accepted
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55136 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:30:31 2026] 127.0.0.1:55136 Closing
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48522 Accepted
+[Fri Jul 31 04:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48522 [200]: GET /api/stats.php
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48522 Closing
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48536 Accepted
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48536 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:31:31 2026] 127.0.0.1:48536 Closing
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33474 Accepted
+[Fri Jul 31 04:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33474 [200]: GET /api/stats.php
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33474 Closing
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33490 Accepted
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33490 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:32:31 2026] 127.0.0.1:33490 Closing
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48456 Accepted
+[Fri Jul 31 04:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48456 [200]: GET /api/stats.php
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48456 Closing
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48462 Accepted
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48462 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:33:31 2026] 127.0.0.1:48462 Closing
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34234 Accepted
+[Fri Jul 31 04:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34234 [200]: GET /api/stats.php
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34234 Closing
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34242 Accepted
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34242 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:34:31 2026] 127.0.0.1:34242 Closing
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47046 Accepted
+[Fri Jul 31 04:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47046 [200]: GET /api/stats.php
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47046 Closing
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47052 Accepted
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47052 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:35:31 2026] 127.0.0.1:47052 Closing
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46016 Accepted
+[Fri Jul 31 04:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46016 [200]: GET /api/stats.php
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46016 Closing
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46026 Accepted
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46026 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:36:31 2026] 127.0.0.1:46026 Closing
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39706 Accepted
+[Fri Jul 31 04:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39706 [200]: GET /api/stats.php
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39706 Closing
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39720 Accepted
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39720 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:37:31 2026] 127.0.0.1:39720 Closing
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49978 Accepted
+[Fri Jul 31 04:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49978 [200]: GET /api/stats.php
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49978 Closing
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49994 Accepted
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49994 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:38:31 2026] 127.0.0.1:49994 Closing
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52832 Accepted
+[Fri Jul 31 04:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52832 [200]: GET /api/stats.php
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52832 Closing
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52846 Accepted
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52846 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:39:31 2026] 127.0.0.1:52846 Closing
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59674 Accepted
+[Fri Jul 31 04:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59674 [200]: GET /api/stats.php
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59674 Closing
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59686 Accepted
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59686 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:40:31 2026] 127.0.0.1:59686 Closing
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58172 Accepted
+[Fri Jul 31 04:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58172 [200]: GET /api/stats.php
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58172 Closing
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58186 Accepted
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58186 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:41:31 2026] 127.0.0.1:58186 Closing
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53242 Accepted
+[Fri Jul 31 04:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53242 [200]: GET /api/stats.php
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53242 Closing
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53250 Accepted
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53250 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:42:31 2026] 127.0.0.1:53250 Closing
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42808 Accepted
+[Fri Jul 31 04:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42808 [200]: GET /api/stats.php
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42808 Closing
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42816 Accepted
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42816 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:43:31 2026] 127.0.0.1:42816 Closing
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48484 Accepted
+[Fri Jul 31 04:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48484 [200]: GET /api/stats.php
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48484 Closing
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48494 Accepted
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48494 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:44:31 2026] 127.0.0.1:48494 Closing
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34986 Accepted
+[Fri Jul 31 04:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34986 [200]: GET /api/stats.php
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34986 Closing
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34990 Accepted
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34990 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:45:31 2026] 127.0.0.1:34990 Closing
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55056 Accepted
+[Fri Jul 31 04:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55056 [200]: GET /api/stats.php
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55056 Closing
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55068 Accepted
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55068 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:46:31 2026] 127.0.0.1:55068 Closing
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37656 Accepted
+[Fri Jul 31 04:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37656 [200]: GET /api/stats.php
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37656 Closing
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37666 Accepted
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37666 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:47:31 2026] 127.0.0.1:37666 Closing
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49970 Accepted
+[Fri Jul 31 04:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49970 [200]: GET /api/stats.php
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49970 Closing
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49972 Accepted
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49972 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:48:31 2026] 127.0.0.1:49972 Closing
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41234 Accepted
+[Fri Jul 31 04:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41234 [200]: GET /api/stats.php
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41234 Closing
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41250 Accepted
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41250 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:49:31 2026] 127.0.0.1:41250 Closing
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54806 Accepted
+[Fri Jul 31 04:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54806 [200]: GET /api/stats.php
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54806 Closing
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54810 Accepted
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54810 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:50:31 2026] 127.0.0.1:54810 Closing
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53308 Accepted
+[Fri Jul 31 04:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53308 [200]: GET /api/stats.php
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53308 Closing
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53318 Accepted
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53318 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:51:31 2026] 127.0.0.1:53318 Closing
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53734 Accepted
+[Fri Jul 31 04:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53734 [200]: GET /api/stats.php
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53734 Closing
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53748 Accepted
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53748 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:52:31 2026] 127.0.0.1:53748 Closing
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59088 Accepted
+[Fri Jul 31 04:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59088 [200]: GET /api/stats.php
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59088 Closing
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59094 Accepted
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59094 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:53:31 2026] 127.0.0.1:59094 Closing
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59542 Accepted
+[Fri Jul 31 04:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59542 [200]: GET /api/stats.php
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59542 Closing
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59546 Accepted
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59546 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:54:31 2026] 127.0.0.1:59546 Closing
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56122 Accepted
+[Fri Jul 31 04:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56122 [200]: GET /api/stats.php
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56122 Closing
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56126 Accepted
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56126 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:55:31 2026] 127.0.0.1:56126 Closing
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37156 Accepted
+[Fri Jul 31 04:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37156 [200]: GET /api/stats.php
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37156 Closing
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37168 Accepted
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37168 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:56:31 2026] 127.0.0.1:37168 Closing
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49130 Accepted
+[Fri Jul 31 04:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49130 [200]: GET /api/stats.php
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49130 Closing
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49132 Accepted
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49132 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:57:31 2026] 127.0.0.1:49132 Closing
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39818 Accepted
+[Fri Jul 31 04:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39818 [200]: GET /api/stats.php
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39818 Closing
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39832 Accepted
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39832 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:58:31 2026] 127.0.0.1:39832 Closing
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48810 Accepted
+[Fri Jul 31 04:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48810 [200]: GET /api/stats.php
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48810 Closing
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48826 Accepted
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48826 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 04:59:31 2026] 127.0.0.1:48826 Closing
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33738 Accepted
+[Fri Jul 31 05:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33738 [200]: GET /api/stats.php
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33738 Closing
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33744 Accepted
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33744 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:00:31 2026] 127.0.0.1:33744 Closing
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60080 Accepted
+[Fri Jul 31 05:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60080 [200]: GET /api/stats.php
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60080 Closing
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60084 Accepted
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60084 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:01:31 2026] 127.0.0.1:60084 Closing
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60048 Accepted
+[Fri Jul 31 05:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60048 [200]: GET /api/stats.php
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60048 Closing
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60064 Accepted
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60064 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:02:31 2026] 127.0.0.1:60064 Closing
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53284 Accepted
+[Fri Jul 31 05:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53284 [200]: GET /api/stats.php
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53284 Closing
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53298 Accepted
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53298 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:03:31 2026] 127.0.0.1:53298 Closing
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47248 Accepted
+[Fri Jul 31 05:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47248 [200]: GET /api/stats.php
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47248 Closing
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47264 Accepted
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47264 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:04:31 2026] 127.0.0.1:47264 Closing
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35490 Accepted
+[Fri Jul 31 05:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35490 [200]: GET /api/stats.php
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35490 Closing
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35502 Accepted
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35502 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:05:31 2026] 127.0.0.1:35502 Closing
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48632 Accepted
+[Fri Jul 31 05:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48632 [200]: GET /api/stats.php
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48632 Closing
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48642 Accepted
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48642 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:06:31 2026] 127.0.0.1:48642 Closing
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45190 Accepted
+[Fri Jul 31 05:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45190 [200]: GET /api/stats.php
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45190 Closing
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45194 Accepted
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45194 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:07:31 2026] 127.0.0.1:45194 Closing
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58516 Accepted
+[Fri Jul 31 05:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58516 [200]: GET /api/stats.php
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58516 Closing
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58532 Accepted
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58532 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:08:31 2026] 127.0.0.1:58532 Closing
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53028 Accepted
+[Fri Jul 31 05:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53028 [200]: GET /api/stats.php
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53028 Closing
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53038 Accepted
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53038 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:09:31 2026] 127.0.0.1:53038 Closing
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36634 Accepted
+[Fri Jul 31 05:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36634 [200]: GET /api/stats.php
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36634 Closing
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36642 Accepted
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36642 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:10:31 2026] 127.0.0.1:36642 Closing
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43188 Accepted
+[Fri Jul 31 05:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43188 [200]: GET /api/stats.php
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43188 Closing
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43204 Accepted
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43204 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:11:31 2026] 127.0.0.1:43204 Closing
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58012 Accepted
+[Fri Jul 31 05:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58012 [200]: GET /api/stats.php
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58012 Closing
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58020 Accepted
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58020 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:12:31 2026] 127.0.0.1:58020 Closing
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48762 Accepted
+[Fri Jul 31 05:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48762 [200]: GET /api/stats.php
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48762 Closing
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48778 Accepted
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48778 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:13:31 2026] 127.0.0.1:48778 Closing
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:53990 Accepted
+[Fri Jul 31 05:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:53990 [200]: GET /api/stats.php
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:53990 Closing
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:54004 Accepted
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:54004 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:14:31 2026] 127.0.0.1:54004 Closing
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44434 Accepted
+[Fri Jul 31 05:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44434 [200]: GET /api/stats.php
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44434 Closing
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44442 Accepted
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44442 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:15:31 2026] 127.0.0.1:44442 Closing
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49684 Accepted
+[Fri Jul 31 05:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49684 [200]: GET /api/stats.php
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49684 Closing
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49694 Accepted
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49694 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:16:31 2026] 127.0.0.1:49694 Closing
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44076 Accepted
+[Fri Jul 31 05:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44076 [200]: GET /api/stats.php
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44076 Closing
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44086 Accepted
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44086 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:17:31 2026] 127.0.0.1:44086 Closing
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33346 Accepted
+[Fri Jul 31 05:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33346 [200]: GET /api/stats.php
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33346 Closing
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33350 Accepted
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33350 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:18:31 2026] 127.0.0.1:33350 Closing
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44610 Accepted
+[Fri Jul 31 05:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44610 [200]: GET /api/stats.php
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44610 Closing
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44622 Accepted
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44622 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:19:31 2026] 127.0.0.1:44622 Closing
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35580 Accepted
+[Fri Jul 31 05:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35580 [200]: GET /api/stats.php
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35580 Closing
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35590 Accepted
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35590 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:20:31 2026] 127.0.0.1:35590 Closing
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42300 Accepted
+[Fri Jul 31 05:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42300 [200]: GET /api/stats.php
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42300 Closing
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42312 Accepted
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42312 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:21:31 2026] 127.0.0.1:42312 Closing
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54222 Accepted
+[Fri Jul 31 05:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54222 [200]: GET /api/stats.php
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54222 Closing
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54228 Accepted
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54228 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:22:31 2026] 127.0.0.1:54228 Closing
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40586 Accepted
+[Fri Jul 31 05:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40586 [200]: GET /api/stats.php
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40586 Closing
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40594 Accepted
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40594 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:23:31 2026] 127.0.0.1:40594 Closing
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40942 Accepted
+[Fri Jul 31 05:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40942 [200]: GET /api/stats.php
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40942 Closing
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40952 Accepted
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40952 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:24:31 2026] 127.0.0.1:40952 Closing
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55210 Accepted
+[Fri Jul 31 05:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55210 [200]: GET /api/stats.php
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55210 Closing
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55214 Accepted
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55214 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:25:31 2026] 127.0.0.1:55214 Closing
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:45990 Accepted
+[Fri Jul 31 05:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:45990 [200]: GET /api/stats.php
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:45990 Closing
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:46002 Accepted
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:46002 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:26:31 2026] 127.0.0.1:46002 Closing
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53766 Accepted
+[Fri Jul 31 05:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53766 [200]: GET /api/stats.php
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53766 Closing
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53782 Accepted
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53782 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:27:31 2026] 127.0.0.1:53782 Closing
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33684 Accepted
+[Fri Jul 31 05:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33684 [200]: GET /api/stats.php
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33684 Closing
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33698 Accepted
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33698 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:28:31 2026] 127.0.0.1:33698 Closing
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60648 Accepted
+[Fri Jul 31 05:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60648 [200]: GET /api/stats.php
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60648 Closing
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60660 Accepted
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60660 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:29:31 2026] 127.0.0.1:60660 Closing
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43388 Accepted
+[Fri Jul 31 05:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43388 [200]: GET /api/stats.php
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43388 Closing
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43400 Accepted
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43400 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:30:31 2026] 127.0.0.1:43400 Closing
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45732 Accepted
+[Fri Jul 31 05:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45732 [200]: GET /api/stats.php
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45732 Closing
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45748 Accepted
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45748 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:31:31 2026] 127.0.0.1:45748 Closing
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58726 Accepted
+[Fri Jul 31 05:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58726 [200]: GET /api/stats.php
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58726 Closing
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58732 Accepted
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58732 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:32:31 2026] 127.0.0.1:58732 Closing
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49020 Accepted
+[Fri Jul 31 05:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49020 [200]: GET /api/stats.php
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49020 Closing
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49028 Accepted
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49028 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:33:31 2026] 127.0.0.1:49028 Closing
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50674 Accepted
+[Fri Jul 31 05:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50674 [200]: GET /api/stats.php
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50674 Closing
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50682 Accepted
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50682 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:34:31 2026] 127.0.0.1:50682 Closing
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50428 Accepted
+[Fri Jul 31 05:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50428 [200]: GET /api/stats.php
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50428 Closing
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50442 Accepted
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50442 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:35:31 2026] 127.0.0.1:50442 Closing
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58464 Accepted
+[Fri Jul 31 05:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58464 [200]: GET /api/stats.php
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58464 Closing
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58470 Accepted
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58470 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:36:31 2026] 127.0.0.1:58470 Closing
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53340 Accepted
+[Fri Jul 31 05:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53340 [200]: GET /api/stats.php
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53340 Closing
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53344 Accepted
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53344 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:37:31 2026] 127.0.0.1:53344 Closing
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49274 Accepted
+[Fri Jul 31 05:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49274 [200]: GET /api/stats.php
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49274 Closing
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49280 Accepted
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49280 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:38:31 2026] 127.0.0.1:49280 Closing
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44982 Accepted
+[Fri Jul 31 05:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44982 [200]: GET /api/stats.php
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44982 Closing
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44990 Accepted
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44990 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:39:31 2026] 127.0.0.1:44990 Closing
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54940 Accepted
+[Fri Jul 31 05:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54940 [200]: GET /api/stats.php
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54940 Closing
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54946 Accepted
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54946 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:40:31 2026] 127.0.0.1:54946 Closing
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55120 Accepted
+[Fri Jul 31 05:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55120 [200]: GET /api/stats.php
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55120 Closing
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55128 Accepted
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55128 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:41:31 2026] 127.0.0.1:55128 Closing
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54370 Accepted
+[Fri Jul 31 05:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54370 [200]: GET /api/stats.php
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54370 Closing
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54372 Accepted
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54372 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:42:31 2026] 127.0.0.1:54372 Closing
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45434 Accepted
+[Fri Jul 31 05:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45434 [200]: GET /api/stats.php
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45434 Closing
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45446 Accepted
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45446 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:43:31 2026] 127.0.0.1:45446 Closing
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40944 Accepted
+[Fri Jul 31 05:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40944 [200]: GET /api/stats.php
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40944 Closing
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40946 Accepted
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40946 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:44:31 2026] 127.0.0.1:40946 Closing
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52374 Accepted
+[Fri Jul 31 05:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52374 [200]: GET /api/stats.php
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52374 Closing
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52388 Accepted
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52388 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:45:31 2026] 127.0.0.1:52388 Closing
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34234 Accepted
+[Fri Jul 31 05:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34234 [200]: GET /api/stats.php
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34234 Closing
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34240 Accepted
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34240 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:46:31 2026] 127.0.0.1:34240 Closing
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42260 Accepted
+[Fri Jul 31 05:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42260 [200]: GET /api/stats.php
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42260 Closing
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42274 Accepted
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42274 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:47:31 2026] 127.0.0.1:42274 Closing
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55802 Accepted
+[Fri Jul 31 05:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55802 [200]: GET /api/stats.php
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55802 Closing
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55808 Accepted
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55808 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:48:31 2026] 127.0.0.1:55808 Closing
+[Fri Jul 31 05:49:31 2026] 127.0.0.1:39522 Accepted
+[Fri Jul 31 05:49:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:49:32 2026] 127.0.0.1:39522 [200]: GET /api/stats.php
+[Fri Jul 31 05:49:32 2026] 127.0.0.1:39522 Closing
+[Fri Jul 31 05:49:32 2026] 127.0.0.1:39526 Accepted
+[Fri Jul 31 05:49:32 2026] 127.0.0.1:39526 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:49:32 2026] 127.0.0.1:39526 Closing
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57086 Accepted
+[Fri Jul 31 05:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57086 [200]: GET /api/stats.php
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57086 Closing
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57096 Accepted
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57096 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:50:31 2026] 127.0.0.1:57096 Closing
+[Fri Jul 31 05:51:31 2026] 127.0.0.1:33556 Accepted
+[Fri Jul 31 05:51:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:51:32 2026] 127.0.0.1:33556 [200]: GET /api/stats.php
+[Fri Jul 31 05:51:32 2026] 127.0.0.1:33556 Closing
+[Fri Jul 31 05:51:32 2026] 127.0.0.1:33560 Accepted
+[Fri Jul 31 05:51:32 2026] 127.0.0.1:33560 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:51:32 2026] 127.0.0.1:33560 Closing
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36388 Accepted
+[Fri Jul 31 05:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36388 [200]: GET /api/stats.php
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36388 Closing
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36402 Accepted
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36402 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:52:31 2026] 127.0.0.1:36402 Closing
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50428 Accepted
+[Fri Jul 31 05:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50428 [200]: GET /api/stats.php
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50428 Closing
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50440 Accepted
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50440 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:53:31 2026] 127.0.0.1:50440 Closing
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47682 Accepted
+[Fri Jul 31 05:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47682 [200]: GET /api/stats.php
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47682 Closing
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47696 Accepted
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47696 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:54:31 2026] 127.0.0.1:47696 Closing
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52456 Accepted
+[Fri Jul 31 05:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52456 [200]: GET /api/stats.php
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52456 Closing
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52470 Accepted
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52470 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:55:31 2026] 127.0.0.1:52470 Closing
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38950 Accepted
+[Fri Jul 31 05:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38950 [200]: GET /api/stats.php
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38950 Closing
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38966 Accepted
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38966 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:56:31 2026] 127.0.0.1:38966 Closing
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54162 Accepted
+[Fri Jul 31 05:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54162 [200]: GET /api/stats.php
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54162 Closing
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54178 Accepted
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54178 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:57:31 2026] 127.0.0.1:54178 Closing
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39850 Accepted
+[Fri Jul 31 05:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39850 [200]: GET /api/stats.php
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39850 Closing
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39860 Accepted
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39860 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:58:31 2026] 127.0.0.1:39860 Closing
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40308 Accepted
+[Fri Jul 31 05:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40308 [200]: GET /api/stats.php
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40308 Closing
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40318 Accepted
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40318 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 05:59:31 2026] 127.0.0.1:40318 Closing
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45114 Accepted
+[Fri Jul 31 06:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45114 [200]: GET /api/stats.php
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45114 Closing
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45130 Accepted
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45130 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:00:31 2026] 127.0.0.1:45130 Closing
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56938 Accepted
+[Fri Jul 31 06:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56938 [200]: GET /api/stats.php
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56938 Closing
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56948 Accepted
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56948 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:01:31 2026] 127.0.0.1:56948 Closing
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46454 Accepted
+[Fri Jul 31 06:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46454 [200]: GET /api/stats.php
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46454 Closing
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46464 Accepted
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46464 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:02:31 2026] 127.0.0.1:46464 Closing
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49222 Accepted
+[Fri Jul 31 06:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49222 [200]: GET /api/stats.php
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49222 Closing
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49224 Accepted
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49224 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:03:31 2026] 127.0.0.1:49224 Closing
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40166 Accepted
+[Fri Jul 31 06:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40166 [200]: GET /api/stats.php
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40166 Closing
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40180 Accepted
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40180 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:04:31 2026] 127.0.0.1:40180 Closing
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38796 Accepted
+[Fri Jul 31 06:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38796 [200]: GET /api/stats.php
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38796 Closing
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38800 Accepted
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38800 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:05:31 2026] 127.0.0.1:38800 Closing
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42282 Accepted
+[Fri Jul 31 06:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42282 [200]: GET /api/stats.php
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42282 Closing
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42290 Accepted
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42290 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:06:31 2026] 127.0.0.1:42290 Closing
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44836 Accepted
+[Fri Jul 31 06:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44836 [200]: GET /api/stats.php
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44836 Closing
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44844 Accepted
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44844 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:07:31 2026] 127.0.0.1:44844 Closing
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39618 Accepted
+[Fri Jul 31 06:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39618 [200]: GET /api/stats.php
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39618 Closing
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39620 Accepted
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39620 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:08:31 2026] 127.0.0.1:39620 Closing
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46682 Accepted
+[Fri Jul 31 06:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46682 [200]: GET /api/stats.php
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46682 Closing
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46694 Accepted
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46694 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:09:31 2026] 127.0.0.1:46694 Closing
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60862 Accepted
+[Fri Jul 31 06:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60862 [200]: GET /api/stats.php
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60862 Closing
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60864 Accepted
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60864 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:10:31 2026] 127.0.0.1:60864 Closing
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53124 Accepted
+[Fri Jul 31 06:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53124 [200]: GET /api/stats.php
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53124 Closing
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53134 Accepted
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53134 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:11:31 2026] 127.0.0.1:53134 Closing
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46200 Accepted
+[Fri Jul 31 06:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46200 [200]: GET /api/stats.php
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46200 Closing
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46212 Accepted
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46212 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:12:31 2026] 127.0.0.1:46212 Closing
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43166 Accepted
+[Fri Jul 31 06:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43166 [200]: GET /api/stats.php
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43166 Closing
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43174 Accepted
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43174 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:13:31 2026] 127.0.0.1:43174 Closing
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55522 Accepted
+[Fri Jul 31 06:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55522 [200]: GET /api/stats.php
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55522 Closing
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55524 Accepted
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55524 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:14:31 2026] 127.0.0.1:55524 Closing
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41800 Accepted
+[Fri Jul 31 06:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41800 [200]: GET /api/stats.php
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41800 Closing
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41808 Accepted
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41808 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:15:31 2026] 127.0.0.1:41808 Closing
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55474 Accepted
+[Fri Jul 31 06:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55474 [200]: GET /api/stats.php
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55474 Closing
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55488 Accepted
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55488 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:16:31 2026] 127.0.0.1:55488 Closing
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47672 Accepted
+[Fri Jul 31 06:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47672 [200]: GET /api/stats.php
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47672 Closing
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47678 Accepted
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47678 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:17:31 2026] 127.0.0.1:47678 Closing
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45736 Accepted
+[Fri Jul 31 06:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45736 [200]: GET /api/stats.php
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45736 Closing
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45740 Accepted
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45740 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:18:31 2026] 127.0.0.1:45740 Closing
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48040 Accepted
+[Fri Jul 31 06:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48040 [200]: GET /api/stats.php
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48040 Closing
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48054 Accepted
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48054 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:19:31 2026] 127.0.0.1:48054 Closing
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44734 Accepted
+[Fri Jul 31 06:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44734 [200]: GET /api/stats.php
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44734 Closing
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44740 Accepted
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44740 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:20:31 2026] 127.0.0.1:44740 Closing
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53694 Accepted
+[Fri Jul 31 06:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53694 [200]: GET /api/stats.php
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53694 Closing
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53706 Accepted
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53706 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:21:31 2026] 127.0.0.1:53706 Closing
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57450 Accepted
+[Fri Jul 31 06:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57450 [200]: GET /api/stats.php
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57450 Closing
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57462 Accepted
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57462 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:22:31 2026] 127.0.0.1:57462 Closing
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60462 Accepted
+[Fri Jul 31 06:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60462 [200]: GET /api/stats.php
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60462 Closing
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60470 Accepted
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60470 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:23:31 2026] 127.0.0.1:60470 Closing
+[Fri Jul 31 06:24:31 2026] 127.0.0.1:55318 Accepted
+[Fri Jul 31 06:24:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:24:32 2026] 127.0.0.1:55318 [200]: GET /api/stats.php
+[Fri Jul 31 06:24:32 2026] 127.0.0.1:55318 Closing
+[Fri Jul 31 06:24:32 2026] 127.0.0.1:55324 Accepted
+[Fri Jul 31 06:24:32 2026] 127.0.0.1:55324 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:24:32 2026] 127.0.0.1:55324 Closing
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53288 Accepted
+[Fri Jul 31 06:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53288 [200]: GET /api/stats.php
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53288 Closing
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53296 Accepted
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53296 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:25:31 2026] 127.0.0.1:53296 Closing
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44018 Accepted
+[Fri Jul 31 06:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44018 [200]: GET /api/stats.php
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44018 Closing
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44022 Accepted
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44022 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:26:31 2026] 127.0.0.1:44022 Closing
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51172 Accepted
+[Fri Jul 31 06:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51172 [200]: GET /api/stats.php
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51172 Closing
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51184 Accepted
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51184 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:27:31 2026] 127.0.0.1:51184 Closing
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50724 Accepted
+[Fri Jul 31 06:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50724 [200]: GET /api/stats.php
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50724 Closing
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50726 Accepted
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50726 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:28:31 2026] 127.0.0.1:50726 Closing
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47802 Accepted
+[Fri Jul 31 06:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47802 [200]: GET /api/stats.php
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47802 Closing
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47814 Accepted
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47814 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:29:31 2026] 127.0.0.1:47814 Closing
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:32994 Accepted
+[Fri Jul 31 06:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:32994 [200]: GET /api/stats.php
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:32994 Closing
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:33002 Accepted
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:33002 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:30:31 2026] 127.0.0.1:33002 Closing
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46084 Accepted
+[Fri Jul 31 06:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46084 [200]: GET /api/stats.php
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46084 Closing
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46092 Accepted
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46092 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:31:31 2026] 127.0.0.1:46092 Closing
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44712 Accepted
+[Fri Jul 31 06:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44712 [200]: GET /api/stats.php
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44712 Closing
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44716 Accepted
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44716 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:32:31 2026] 127.0.0.1:44716 Closing
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39796 Accepted
+[Fri Jul 31 06:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39796 [200]: GET /api/stats.php
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39796 Closing
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39812 Accepted
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39812 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:33:31 2026] 127.0.0.1:39812 Closing
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37864 Accepted
+[Fri Jul 31 06:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37864 [200]: GET /api/stats.php
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37864 Closing
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37870 Accepted
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37870 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:34:31 2026] 127.0.0.1:37870 Closing
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58260 Accepted
+[Fri Jul 31 06:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58260 [200]: GET /api/stats.php
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58260 Closing
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58266 Accepted
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58266 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:35:31 2026] 127.0.0.1:58266 Closing
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43054 Accepted
+[Fri Jul 31 06:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43054 [200]: GET /api/stats.php
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43054 Closing
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43070 Accepted
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43070 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:36:31 2026] 127.0.0.1:43070 Closing
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40804 Accepted
+[Fri Jul 31 06:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40804 [200]: GET /api/stats.php
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40804 Closing
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40820 Accepted
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40820 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:37:31 2026] 127.0.0.1:40820 Closing
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47148 Accepted
+[Fri Jul 31 06:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47148 [200]: GET /api/stats.php
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47148 Closing
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47154 Accepted
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47154 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:38:31 2026] 127.0.0.1:47154 Closing
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45730 Accepted
+[Fri Jul 31 06:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45730 [200]: GET /api/stats.php
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45730 Closing
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45746 Accepted
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45746 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:39:31 2026] 127.0.0.1:45746 Closing
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39428 Accepted
+[Fri Jul 31 06:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39428 [200]: GET /api/stats.php
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39428 Closing
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39436 Accepted
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39436 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:40:31 2026] 127.0.0.1:39436 Closing
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48588 Accepted
+[Fri Jul 31 06:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48588 [200]: GET /api/stats.php
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48588 Closing
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48602 Accepted
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48602 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:41:31 2026] 127.0.0.1:48602 Closing
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50170 Accepted
+[Fri Jul 31 06:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50170 [200]: GET /api/stats.php
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50170 Closing
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50174 Accepted
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50174 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:42:31 2026] 127.0.0.1:50174 Closing
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37326 Accepted
+[Fri Jul 31 06:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37326 [200]: GET /api/stats.php
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37326 Closing
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37336 Accepted
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37336 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:43:31 2026] 127.0.0.1:37336 Closing
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54738 Accepted
+[Fri Jul 31 06:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54738 [200]: GET /api/stats.php
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54738 Closing
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54750 Accepted
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54750 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:44:31 2026] 127.0.0.1:54750 Closing
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35736 Accepted
+[Fri Jul 31 06:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35736 [200]: GET /api/stats.php
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35736 Closing
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35738 Accepted
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35738 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:45:31 2026] 127.0.0.1:35738 Closing
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39146 Accepted
+[Fri Jul 31 06:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39146 [200]: GET /api/stats.php
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39146 Closing
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39162 Accepted
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39162 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:46:31 2026] 127.0.0.1:39162 Closing
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34832 Accepted
+[Fri Jul 31 06:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34832 [200]: GET /api/stats.php
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34832 Closing
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34838 Accepted
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34838 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:47:31 2026] 127.0.0.1:34838 Closing
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48090 Accepted
+[Fri Jul 31 06:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48090 [200]: GET /api/stats.php
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48090 Closing
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48106 Accepted
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48106 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:48:31 2026] 127.0.0.1:48106 Closing
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42072 Accepted
+[Fri Jul 31 06:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42072 [200]: GET /api/stats.php
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42072 Closing
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42084 Accepted
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42084 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:49:31 2026] 127.0.0.1:42084 Closing
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44246 Accepted
+[Fri Jul 31 06:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44246 [200]: GET /api/stats.php
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44246 Closing
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44248 Accepted
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44248 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:50:31 2026] 127.0.0.1:44248 Closing
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38754 Accepted
+[Fri Jul 31 06:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38754 [200]: GET /api/stats.php
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38754 Closing
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38766 Accepted
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38766 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:51:31 2026] 127.0.0.1:38766 Closing
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44880 Accepted
+[Fri Jul 31 06:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44880 [200]: GET /api/stats.php
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44880 Closing
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44886 Accepted
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44886 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:52:31 2026] 127.0.0.1:44886 Closing
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37674 Accepted
+[Fri Jul 31 06:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37674 [200]: GET /api/stats.php
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37674 Closing
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37686 Accepted
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37686 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:53:31 2026] 127.0.0.1:37686 Closing
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59606 Accepted
+[Fri Jul 31 06:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59606 [200]: GET /api/stats.php
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59606 Closing
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59610 Accepted
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59610 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:54:31 2026] 127.0.0.1:59610 Closing
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53642 Accepted
+[Fri Jul 31 06:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53642 [200]: GET /api/stats.php
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53642 Closing
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53648 Accepted
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53648 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:55:31 2026] 127.0.0.1:53648 Closing
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50518 Accepted
+[Fri Jul 31 06:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50518 [200]: GET /api/stats.php
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50518 Closing
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50526 Accepted
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50526 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:56:31 2026] 127.0.0.1:50526 Closing
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49152 Accepted
+[Fri Jul 31 06:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49152 [200]: GET /api/stats.php
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49152 Closing
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49164 Accepted
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49164 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:57:31 2026] 127.0.0.1:49164 Closing
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41878 Accepted
+[Fri Jul 31 06:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41878 [200]: GET /api/stats.php
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41878 Closing
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41894 Accepted
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41894 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:58:31 2026] 127.0.0.1:41894 Closing
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36980 Accepted
+[Fri Jul 31 06:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36980 [200]: GET /api/stats.php
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36980 Closing
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36996 Accepted
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36996 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 06:59:31 2026] 127.0.0.1:36996 Closing
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60602 Accepted
+[Fri Jul 31 07:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60602 [200]: GET /api/stats.php
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60602 Closing
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60618 Accepted
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60618 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:00:31 2026] 127.0.0.1:60618 Closing
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35804 Accepted
+[Fri Jul 31 07:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35804 [200]: GET /api/stats.php
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35804 Closing
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35812 Accepted
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35812 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:01:31 2026] 127.0.0.1:35812 Closing
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55148 Accepted
+[Fri Jul 31 07:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55148 [200]: GET /api/stats.php
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55148 Closing
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55154 Accepted
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55154 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:02:31 2026] 127.0.0.1:55154 Closing
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52830 Accepted
+[Fri Jul 31 07:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52830 [200]: GET /api/stats.php
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52830 Closing
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52840 Accepted
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52840 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:03:31 2026] 127.0.0.1:52840 Closing
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38374 Accepted
+[Fri Jul 31 07:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38374 [200]: GET /api/stats.php
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38374 Closing
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38384 Accepted
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38384 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:04:31 2026] 127.0.0.1:38384 Closing
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35136 Accepted
+[Fri Jul 31 07:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35136 [200]: GET /api/stats.php
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35136 Closing
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35148 Accepted
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35148 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:05:31 2026] 127.0.0.1:35148 Closing
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52876 Accepted
+[Fri Jul 31 07:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52876 [200]: GET /api/stats.php
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52876 Closing
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52886 Accepted
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52886 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:06:31 2026] 127.0.0.1:52886 Closing
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38704 Accepted
+[Fri Jul 31 07:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38704 [200]: GET /api/stats.php
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38704 Closing
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38706 Accepted
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38706 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:07:31 2026] 127.0.0.1:38706 Closing
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43450 Accepted
+[Fri Jul 31 07:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43450 [200]: GET /api/stats.php
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43450 Closing
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43456 Accepted
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43456 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:08:31 2026] 127.0.0.1:43456 Closing
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41610 Accepted
+[Fri Jul 31 07:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41610 [200]: GET /api/stats.php
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41610 Closing
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41620 Accepted
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41620 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:09:31 2026] 127.0.0.1:41620 Closing
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35892 Accepted
+[Fri Jul 31 07:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35892 [200]: GET /api/stats.php
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35892 Closing
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35908 Accepted
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35908 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:10:31 2026] 127.0.0.1:35908 Closing
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52240 Accepted
+[Fri Jul 31 07:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52240 [200]: GET /api/stats.php
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52240 Closing
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52248 Accepted
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52248 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:11:31 2026] 127.0.0.1:52248 Closing
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47752 Accepted
+[Fri Jul 31 07:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47752 [200]: GET /api/stats.php
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47752 Closing
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47764 Accepted
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47764 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:12:31 2026] 127.0.0.1:47764 Closing
+[Fri Jul 31 07:13:31 2026] 127.0.0.1:37098 Accepted
+[Fri Jul 31 07:13:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:13:32 2026] 127.0.0.1:37098 [200]: GET /api/stats.php
+[Fri Jul 31 07:13:32 2026] 127.0.0.1:37098 Closing
+[Fri Jul 31 07:13:32 2026] 127.0.0.1:37100 Accepted
+[Fri Jul 31 07:13:32 2026] 127.0.0.1:37100 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:13:32 2026] 127.0.0.1:37100 Closing
+[Fri Jul 31 07:14:31 2026] 127.0.0.1:59328 Accepted
+[Fri Jul 31 07:14:32 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:14:32 2026] 127.0.0.1:59328 [200]: GET /api/stats.php
+[Fri Jul 31 07:14:32 2026] 127.0.0.1:59328 Closing
+[Fri Jul 31 07:14:32 2026] 127.0.0.1:59336 Accepted
+[Fri Jul 31 07:14:32 2026] 127.0.0.1:59336 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:14:32 2026] 127.0.0.1:59336 Closing
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39814 Accepted
+[Fri Jul 31 07:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39814 [200]: GET /api/stats.php
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39814 Closing
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39824 Accepted
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39824 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:15:31 2026] 127.0.0.1:39824 Closing
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33830 Accepted
+[Fri Jul 31 07:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33830 [200]: GET /api/stats.php
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33830 Closing
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33844 Accepted
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33844 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:16:31 2026] 127.0.0.1:33844 Closing
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55292 Accepted
+[Fri Jul 31 07:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55292 [200]: GET /api/stats.php
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55292 Closing
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55300 Accepted
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55300 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:17:31 2026] 127.0.0.1:55300 Closing
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42152 Accepted
+[Fri Jul 31 07:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42152 [200]: GET /api/stats.php
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42152 Closing
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42156 Accepted
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42156 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:18:31 2026] 127.0.0.1:42156 Closing
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46152 Accepted
+[Fri Jul 31 07:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46152 [200]: GET /api/stats.php
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46152 Closing
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46158 Accepted
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46158 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:19:31 2026] 127.0.0.1:46158 Closing
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39780 Accepted
+[Fri Jul 31 07:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39780 [200]: GET /api/stats.php
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39780 Closing
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39784 Accepted
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39784 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:20:31 2026] 127.0.0.1:39784 Closing
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57494 Accepted
+[Fri Jul 31 07:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57494 [200]: GET /api/stats.php
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57494 Closing
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57502 Accepted
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57502 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:21:31 2026] 127.0.0.1:57502 Closing
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33450 Accepted
+[Fri Jul 31 07:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33450 [200]: GET /api/stats.php
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33450 Closing
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33456 Accepted
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33456 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:22:31 2026] 127.0.0.1:33456 Closing
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42494 Accepted
+[Fri Jul 31 07:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42494 [200]: GET /api/stats.php
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42494 Closing
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42502 Accepted
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42502 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:23:31 2026] 127.0.0.1:42502 Closing
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54982 Accepted
+[Fri Jul 31 07:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54982 [200]: GET /api/stats.php
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54982 Closing
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54992 Accepted
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54992 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:24:31 2026] 127.0.0.1:54992 Closing
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40792 Accepted
+[Fri Jul 31 07:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40792 [200]: GET /api/stats.php
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40792 Closing
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40808 Accepted
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40808 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:25:31 2026] 127.0.0.1:40808 Closing
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52088 Accepted
+[Fri Jul 31 07:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52088 [200]: GET /api/stats.php
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52088 Closing
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52094 Accepted
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52094 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:26:31 2026] 127.0.0.1:52094 Closing
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35152 Accepted
+[Fri Jul 31 07:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35152 [200]: GET /api/stats.php
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35152 Closing
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35166 Accepted
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35166 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:27:31 2026] 127.0.0.1:35166 Closing
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36298 Accepted
+[Fri Jul 31 07:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36298 [200]: GET /api/stats.php
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36298 Closing
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36308 Accepted
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36308 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:28:31 2026] 127.0.0.1:36308 Closing
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53614 Accepted
+[Fri Jul 31 07:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53614 [200]: GET /api/stats.php
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53614 Closing
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53626 Accepted
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53626 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:29:31 2026] 127.0.0.1:53626 Closing
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42010 Accepted
+[Fri Jul 31 07:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42010 [200]: GET /api/stats.php
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42010 Closing
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42024 Accepted
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42024 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:30:31 2026] 127.0.0.1:42024 Closing
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59192 Accepted
+[Fri Jul 31 07:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59192 [200]: GET /api/stats.php
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59192 Closing
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59208 Accepted
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59208 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:31:31 2026] 127.0.0.1:59208 Closing
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58754 Accepted
+[Fri Jul 31 07:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58754 [200]: GET /api/stats.php
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58754 Closing
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58760 Accepted
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58760 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:32:31 2026] 127.0.0.1:58760 Closing
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42626 Accepted
+[Fri Jul 31 07:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42626 [200]: GET /api/stats.php
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42626 Closing
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42632 Accepted
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42632 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:33:31 2026] 127.0.0.1:42632 Closing
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36186 Accepted
+[Fri Jul 31 07:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36186 [200]: GET /api/stats.php
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36186 Closing
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36188 Accepted
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36188 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:34:31 2026] 127.0.0.1:36188 Closing
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34264 Accepted
+[Fri Jul 31 07:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34264 [200]: GET /api/stats.php
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34264 Closing
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34274 Accepted
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34274 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:35:31 2026] 127.0.0.1:34274 Closing
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47346 Accepted
+[Fri Jul 31 07:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47346 [200]: GET /api/stats.php
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47346 Closing
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47350 Accepted
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47350 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:36:31 2026] 127.0.0.1:47350 Closing
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53428 Accepted
+[Fri Jul 31 07:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53428 [200]: GET /api/stats.php
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53428 Closing
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53436 Accepted
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53436 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:37:31 2026] 127.0.0.1:53436 Closing
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53382 Accepted
+[Fri Jul 31 07:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53382 [200]: GET /api/stats.php
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53382 Closing
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53390 Accepted
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53390 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:38:31 2026] 127.0.0.1:53390 Closing
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42630 Accepted
+[Fri Jul 31 07:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42630 [200]: GET /api/stats.php
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42630 Closing
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42642 Accepted
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42642 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:39:31 2026] 127.0.0.1:42642 Closing
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44980 Accepted
+[Fri Jul 31 07:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44980 [200]: GET /api/stats.php
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44980 Closing
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44986 Accepted
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44986 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:40:31 2026] 127.0.0.1:44986 Closing
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35490 Accepted
+[Fri Jul 31 07:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35490 [200]: GET /api/stats.php
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35490 Closing
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35506 Accepted
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35506 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:41:31 2026] 127.0.0.1:35506 Closing
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49948 Accepted
+[Fri Jul 31 07:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49948 [200]: GET /api/stats.php
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49948 Closing
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49956 Accepted
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49956 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:42:31 2026] 127.0.0.1:49956 Closing
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34852 Accepted
+[Fri Jul 31 07:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34852 [200]: GET /api/stats.php
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34852 Closing
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34860 Accepted
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34860 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:43:31 2026] 127.0.0.1:34860 Closing
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51464 Accepted
+[Fri Jul 31 07:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51464 [200]: GET /api/stats.php
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51464 Closing
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51470 Accepted
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51470 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:44:31 2026] 127.0.0.1:51470 Closing
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43732 Accepted
+[Fri Jul 31 07:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43732 [200]: GET /api/stats.php
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43732 Closing
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43744 Accepted
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43744 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:45:31 2026] 127.0.0.1:43744 Closing
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42686 Accepted
+[Fri Jul 31 07:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42686 [200]: GET /api/stats.php
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42686 Closing
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42696 Accepted
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42696 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:46:31 2026] 127.0.0.1:42696 Closing
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46908 Accepted
+[Fri Jul 31 07:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46908 [200]: GET /api/stats.php
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46908 Closing
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46914 Accepted
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46914 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:47:31 2026] 127.0.0.1:46914 Closing
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59356 Accepted
+[Fri Jul 31 07:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59356 [200]: GET /api/stats.php
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59356 Closing
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59358 Accepted
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59358 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:48:31 2026] 127.0.0.1:59358 Closing
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48138 Accepted
+[Fri Jul 31 07:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48138 [200]: GET /api/stats.php
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48138 Closing
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48154 Accepted
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48154 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:49:31 2026] 127.0.0.1:48154 Closing
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35348 Accepted
+[Fri Jul 31 07:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35348 [200]: GET /api/stats.php
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35348 Closing
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35360 Accepted
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35360 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:50:31 2026] 127.0.0.1:35360 Closing
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46298 Accepted
+[Fri Jul 31 07:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46298 [200]: GET /api/stats.php
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46298 Closing
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46314 Accepted
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46314 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:51:31 2026] 127.0.0.1:46314 Closing
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45096 Accepted
+[Fri Jul 31 07:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45096 [200]: GET /api/stats.php
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45096 Closing
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45104 Accepted
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45104 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:52:31 2026] 127.0.0.1:45104 Closing
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53792 Accepted
+[Fri Jul 31 07:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53792 [200]: GET /api/stats.php
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53792 Closing
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53808 Accepted
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53808 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:53:31 2026] 127.0.0.1:53808 Closing
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39502 Accepted
+[Fri Jul 31 07:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39502 [200]: GET /api/stats.php
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39502 Closing
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39504 Accepted
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39504 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:54:31 2026] 127.0.0.1:39504 Closing
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53160 Accepted
+[Fri Jul 31 07:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53160 [200]: GET /api/stats.php
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53160 Closing
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53162 Accepted
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53162 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:55:31 2026] 127.0.0.1:53162 Closing
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37258 Accepted
+[Fri Jul 31 07:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37258 [200]: GET /api/stats.php
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37258 Closing
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37274 Accepted
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37274 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:56:31 2026] 127.0.0.1:37274 Closing
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43956 Accepted
+[Fri Jul 31 07:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43956 [200]: GET /api/stats.php
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43956 Closing
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43968 Accepted
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43968 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:57:31 2026] 127.0.0.1:43968 Closing
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39712 Accepted
+[Fri Jul 31 07:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39712 [200]: GET /api/stats.php
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39712 Closing
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39726 Accepted
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39726 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:58:31 2026] 127.0.0.1:39726 Closing
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34258 Accepted
+[Fri Jul 31 07:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34258 [200]: GET /api/stats.php
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34258 Closing
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34264 Accepted
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34264 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 07:59:31 2026] 127.0.0.1:34264 Closing
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40328 Accepted
+[Fri Jul 31 08:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40328 [200]: GET /api/stats.php
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40328 Closing
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40336 Accepted
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40336 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:00:31 2026] 127.0.0.1:40336 Closing
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44940 Accepted
+[Fri Jul 31 08:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44940 [200]: GET /api/stats.php
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44940 Closing
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44948 Accepted
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44948 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:01:31 2026] 127.0.0.1:44948 Closing
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45588 Accepted
+[Fri Jul 31 08:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45588 [200]: GET /api/stats.php
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45588 Closing
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45592 Accepted
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45592 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:02:31 2026] 127.0.0.1:45592 Closing
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59462 Accepted
+[Fri Jul 31 08:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59462 [200]: GET /api/stats.php
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59462 Closing
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59478 Accepted
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59478 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:03:31 2026] 127.0.0.1:59478 Closing
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55778 Accepted
+[Fri Jul 31 08:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55778 [200]: GET /api/stats.php
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55778 Closing
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55780 Accepted
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55780 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:04:31 2026] 127.0.0.1:55780 Closing
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55720 Accepted
+[Fri Jul 31 08:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55720 [200]: GET /api/stats.php
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55720 Closing
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55730 Accepted
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55730 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:05:31 2026] 127.0.0.1:55730 Closing
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40922 Accepted
+[Fri Jul 31 08:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40922 [200]: GET /api/stats.php
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40922 Closing
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40926 Accepted
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40926 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:06:31 2026] 127.0.0.1:40926 Closing
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53550 Accepted
+[Fri Jul 31 08:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53550 [200]: GET /api/stats.php
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53550 Closing
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53562 Accepted
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53562 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:07:31 2026] 127.0.0.1:53562 Closing
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57290 Accepted
+[Fri Jul 31 08:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57290 [200]: GET /api/stats.php
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57290 Closing
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57306 Accepted
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57306 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:08:31 2026] 127.0.0.1:57306 Closing
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48280 Accepted
+[Fri Jul 31 08:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48280 [200]: GET /api/stats.php
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48280 Closing
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48284 Accepted
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48284 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:09:31 2026] 127.0.0.1:48284 Closing
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57290 Accepted
+[Fri Jul 31 08:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57290 [200]: GET /api/stats.php
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57290 Closing
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57300 Accepted
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57300 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:10:31 2026] 127.0.0.1:57300 Closing
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45020 Accepted
+[Fri Jul 31 08:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45020 [200]: GET /api/stats.php
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45020 Closing
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45034 Accepted
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45034 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:11:31 2026] 127.0.0.1:45034 Closing
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43638 Accepted
+[Fri Jul 31 08:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43638 [200]: GET /api/stats.php
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43638 Closing
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43652 Accepted
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43652 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:12:31 2026] 127.0.0.1:43652 Closing
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60410 Accepted
+[Fri Jul 31 08:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60410 [200]: GET /api/stats.php
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60410 Closing
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60412 Accepted
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60412 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:13:31 2026] 127.0.0.1:60412 Closing
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35036 Accepted
+[Fri Jul 31 08:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35036 [200]: GET /api/stats.php
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35036 Closing
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35040 Accepted
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35040 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:14:31 2026] 127.0.0.1:35040 Closing
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48558 Accepted
+[Fri Jul 31 08:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48558 [200]: GET /api/stats.php
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48558 Closing
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48570 Accepted
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48570 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:15:31 2026] 127.0.0.1:48570 Closing
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52082 Accepted
+[Fri Jul 31 08:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52082 [200]: GET /api/stats.php
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52082 Closing
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52088 Accepted
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52088 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:16:31 2026] 127.0.0.1:52088 Closing
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34198 Accepted
+[Fri Jul 31 08:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34198 [200]: GET /api/stats.php
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34198 Closing
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34200 Accepted
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34200 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:17:31 2026] 127.0.0.1:34200 Closing
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57138 Accepted
+[Fri Jul 31 08:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57138 [200]: GET /api/stats.php
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57138 Closing
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57146 Accepted
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57146 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:18:31 2026] 127.0.0.1:57146 Closing
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46020 Accepted
+[Fri Jul 31 08:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46020 [200]: GET /api/stats.php
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46020 Closing
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46036 Accepted
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46036 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:19:31 2026] 127.0.0.1:46036 Closing
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58628 Accepted
+[Fri Jul 31 08:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58628 [200]: GET /api/stats.php
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58628 Closing
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58640 Accepted
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58640 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:20:31 2026] 127.0.0.1:58640 Closing
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46794 Accepted
+[Fri Jul 31 08:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46794 [200]: GET /api/stats.php
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46794 Closing
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46806 Accepted
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46806 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:21:31 2026] 127.0.0.1:46806 Closing
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58544 Accepted
+[Fri Jul 31 08:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58544 [200]: GET /api/stats.php
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58544 Closing
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58558 Accepted
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58558 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:22:31 2026] 127.0.0.1:58558 Closing
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46598 Accepted
+[Fri Jul 31 08:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46598 [200]: GET /api/stats.php
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46598 Closing
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46614 Accepted
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46614 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:23:31 2026] 127.0.0.1:46614 Closing
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54308 Accepted
+[Fri Jul 31 08:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54308 [200]: GET /api/stats.php
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54308 Closing
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54324 Accepted
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54324 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:24:31 2026] 127.0.0.1:54324 Closing
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40858 Accepted
+[Fri Jul 31 08:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40858 [200]: GET /api/stats.php
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40858 Closing
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40864 Accepted
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40864 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:25:31 2026] 127.0.0.1:40864 Closing
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56086 Accepted
+[Fri Jul 31 08:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56086 [200]: GET /api/stats.php
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56086 Closing
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56100 Accepted
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56100 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:26:31 2026] 127.0.0.1:56100 Closing
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44360 Accepted
+[Fri Jul 31 08:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44360 [200]: GET /api/stats.php
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44360 Closing
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44362 Accepted
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44362 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:27:31 2026] 127.0.0.1:44362 Closing
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59070 Accepted
+[Fri Jul 31 08:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59070 [200]: GET /api/stats.php
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59070 Closing
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59072 Accepted
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59072 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:28:31 2026] 127.0.0.1:59072 Closing
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34250 Accepted
+[Fri Jul 31 08:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34250 [200]: GET /api/stats.php
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34250 Closing
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34266 Accepted
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34266 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:29:31 2026] 127.0.0.1:34266 Closing
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42796 Accepted
+[Fri Jul 31 08:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42796 [200]: GET /api/stats.php
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42796 Closing
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42808 Accepted
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42808 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:30:31 2026] 127.0.0.1:42808 Closing
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56928 Accepted
+[Fri Jul 31 08:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56928 [200]: GET /api/stats.php
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56928 Closing
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56930 Accepted
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56930 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:31:31 2026] 127.0.0.1:56930 Closing
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:51990 Accepted
+[Fri Jul 31 08:32:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:51990 [200]: GET /api/stats.php
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:51990 Closing
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:52000 Accepted
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:52000 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:32:31 2026] 127.0.0.1:52000 Closing
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38836 Accepted
+[Fri Jul 31 08:33:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38836 [200]: GET /api/stats.php
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38836 Closing
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38850 Accepted
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38850 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:33:31 2026] 127.0.0.1:38850 Closing
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42584 Accepted
+[Fri Jul 31 08:34:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42584 [200]: GET /api/stats.php
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42584 Closing
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42586 Accepted
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42586 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:34:31 2026] 127.0.0.1:42586 Closing
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37554 Accepted
+[Fri Jul 31 08:35:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37554 [200]: GET /api/stats.php
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37554 Closing
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37568 Accepted
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37568 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:35:31 2026] 127.0.0.1:37568 Closing
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41372 Accepted
+[Fri Jul 31 08:36:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41372 [200]: GET /api/stats.php
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41372 Closing
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41382 Accepted
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41382 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:36:31 2026] 127.0.0.1:41382 Closing
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33528 Accepted
+[Fri Jul 31 08:37:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33528 [200]: GET /api/stats.php
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33528 Closing
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33538 Accepted
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33538 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:37:31 2026] 127.0.0.1:33538 Closing
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58010 Accepted
+[Fri Jul 31 08:38:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58010 [200]: GET /api/stats.php
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58010 Closing
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58024 Accepted
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58024 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:38:31 2026] 127.0.0.1:58024 Closing
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57172 Accepted
+[Fri Jul 31 08:39:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57172 [200]: GET /api/stats.php
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57172 Closing
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57182 Accepted
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57182 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:39:31 2026] 127.0.0.1:57182 Closing
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55452 Accepted
+[Fri Jul 31 08:40:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55452 [200]: GET /api/stats.php
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55452 Closing
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55464 Accepted
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55464 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:40:31 2026] 127.0.0.1:55464 Closing
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57122 Accepted
+[Fri Jul 31 08:41:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57122 [200]: GET /api/stats.php
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57122 Closing
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57128 Accepted
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57128 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:41:31 2026] 127.0.0.1:57128 Closing
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58280 Accepted
+[Fri Jul 31 08:42:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58280 [200]: GET /api/stats.php
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58280 Closing
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58292 Accepted
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58292 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:42:31 2026] 127.0.0.1:58292 Closing
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52696 Accepted
+[Fri Jul 31 08:43:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52696 [200]: GET /api/stats.php
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52696 Closing
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52698 Accepted
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52698 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:43:31 2026] 127.0.0.1:52698 Closing
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39800 Accepted
+[Fri Jul 31 08:44:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39800 [200]: GET /api/stats.php
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39800 Closing
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39812 Accepted
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39812 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:44:31 2026] 127.0.0.1:39812 Closing
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35890 Accepted
+[Fri Jul 31 08:45:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35890 [200]: GET /api/stats.php
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35890 Closing
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35898 Accepted
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35898 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:45:31 2026] 127.0.0.1:35898 Closing
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38126 Accepted
+[Fri Jul 31 08:46:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38126 [200]: GET /api/stats.php
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38126 Closing
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38130 Accepted
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38130 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:46:31 2026] 127.0.0.1:38130 Closing
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50204 Accepted
+[Fri Jul 31 08:47:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50204 [200]: GET /api/stats.php
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50204 Closing
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50210 Accepted
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50210 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:47:31 2026] 127.0.0.1:50210 Closing
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33522 Accepted
+[Fri Jul 31 08:48:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33522 [200]: GET /api/stats.php
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33522 Closing
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33538 Accepted
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33538 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:48:31 2026] 127.0.0.1:33538 Closing
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55328 Accepted
+[Fri Jul 31 08:49:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55328 [200]: GET /api/stats.php
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55328 Closing
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55334 Accepted
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55334 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:49:31 2026] 127.0.0.1:55334 Closing
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34442 Accepted
+[Fri Jul 31 08:50:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34442 [200]: GET /api/stats.php
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34442 Closing
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34452 Accepted
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34452 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:50:31 2026] 127.0.0.1:34452 Closing
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59908 Accepted
+[Fri Jul 31 08:51:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59908 [200]: GET /api/stats.php
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59908 Closing
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59920 Accepted
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59920 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:51:31 2026] 127.0.0.1:59920 Closing
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48262 Accepted
+[Fri Jul 31 08:52:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48262 [200]: GET /api/stats.php
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48262 Closing
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48276 Accepted
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48276 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:52:31 2026] 127.0.0.1:48276 Closing
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48868 Accepted
+[Fri Jul 31 08:53:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48868 [200]: GET /api/stats.php
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48868 Closing
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48876 Accepted
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48876 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:53:31 2026] 127.0.0.1:48876 Closing
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44830 Accepted
+[Fri Jul 31 08:54:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44830 [200]: GET /api/stats.php
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44830 Closing
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44836 Accepted
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44836 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:54:31 2026] 127.0.0.1:44836 Closing
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34194 Accepted
+[Fri Jul 31 08:55:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34194 [200]: GET /api/stats.php
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34194 Closing
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34202 Accepted
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34202 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:55:31 2026] 127.0.0.1:34202 Closing
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59566 Accepted
+[Fri Jul 31 08:56:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59566 [200]: GET /api/stats.php
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59566 Closing
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59570 Accepted
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59570 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:56:31 2026] 127.0.0.1:59570 Closing
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47384 Accepted
+[Fri Jul 31 08:57:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47384 [200]: GET /api/stats.php
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47384 Closing
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47398 Accepted
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47398 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:57:31 2026] 127.0.0.1:47398 Closing
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41058 Accepted
+[Fri Jul 31 08:58:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41058 [200]: GET /api/stats.php
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41058 Closing
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41072 Accepted
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41072 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:58:31 2026] 127.0.0.1:41072 Closing
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43990 Accepted
+[Fri Jul 31 08:59:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43990 [200]: GET /api/stats.php
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43990 Closing
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43994 Accepted
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43994 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 08:59:31 2026] 127.0.0.1:43994 Closing
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36628 Accepted
+[Fri Jul 31 09:00:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36628 [200]: GET /api/stats.php
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36628 Closing
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36640 Accepted
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36640 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:00:31 2026] 127.0.0.1:36640 Closing
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44818 Accepted
+[Fri Jul 31 09:01:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44818 [200]: GET /api/stats.php
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44818 Closing
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44834 Accepted
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44834 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:01:31 2026] 127.0.0.1:44834 Closing
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33138 Accepted
+[Fri Jul 31 09:02:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33138 [200]: GET /api/stats.php
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33138 Closing
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33154 Accepted
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33154 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:02:31 2026] 127.0.0.1:33154 Closing
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42756 Accepted
+[Fri Jul 31 09:03:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42756 [200]: GET /api/stats.php
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42756 Closing
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42770 Accepted
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42770 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:03:31 2026] 127.0.0.1:42770 Closing
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43708 Accepted
+[Fri Jul 31 09:04:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43708 [200]: GET /api/stats.php
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43708 Closing
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43722 Accepted
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43722 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:04:31 2026] 127.0.0.1:43722 Closing
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51754 Accepted
+[Fri Jul 31 09:05:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51754 [200]: GET /api/stats.php
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51754 Closing
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51764 Accepted
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51764 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:05:31 2026] 127.0.0.1:51764 Closing
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34482 Accepted
+[Fri Jul 31 09:06:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34482 [200]: GET /api/stats.php
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34482 Closing
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34494 Accepted
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34494 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:06:31 2026] 127.0.0.1:34494 Closing
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33288 Accepted
+[Fri Jul 31 09:07:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33288 [200]: GET /api/stats.php
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33288 Closing
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33300 Accepted
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33300 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:07:31 2026] 127.0.0.1:33300 Closing
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42550 Accepted
+[Fri Jul 31 09:08:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42550 [200]: GET /api/stats.php
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42550 Closing
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42554 Accepted
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42554 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:08:31 2026] 127.0.0.1:42554 Closing
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48012 Accepted
+[Fri Jul 31 09:09:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48012 [200]: GET /api/stats.php
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48012 Closing
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48028 Accepted
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48028 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:09:31 2026] 127.0.0.1:48028 Closing
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60854 Accepted
+[Fri Jul 31 09:10:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60854 [200]: GET /api/stats.php
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60854 Closing
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60860 Accepted
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60860 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:10:31 2026] 127.0.0.1:60860 Closing
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58988 Accepted
+[Fri Jul 31 09:11:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58988 [200]: GET /api/stats.php
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58988 Closing
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58992 Accepted
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58992 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:11:31 2026] 127.0.0.1:58992 Closing
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48662 Accepted
+[Fri Jul 31 09:12:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48662 [200]: GET /api/stats.php
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48662 Closing
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48668 Accepted
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48668 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:12:31 2026] 127.0.0.1:48668 Closing
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53058 Accepted
+[Fri Jul 31 09:13:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53058 [200]: GET /api/stats.php
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53058 Closing
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53070 Accepted
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53070 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:13:31 2026] 127.0.0.1:53070 Closing
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49906 Accepted
+[Fri Jul 31 09:14:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49906 [200]: GET /api/stats.php
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49906 Closing
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49922 Accepted
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49922 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:14:31 2026] 127.0.0.1:49922 Closing
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60850 Accepted
+[Fri Jul 31 09:15:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60850 [200]: GET /api/stats.php
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60850 Closing
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60866 Accepted
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60866 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:15:31 2026] 127.0.0.1:60866 Closing
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54182 Accepted
+[Fri Jul 31 09:16:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54182 [200]: GET /api/stats.php
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54182 Closing
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54186 Accepted
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54186 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:16:31 2026] 127.0.0.1:54186 Closing
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56804 Accepted
+[Fri Jul 31 09:17:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56804 [200]: GET /api/stats.php
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56804 Closing
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56812 Accepted
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56812 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:17:31 2026] 127.0.0.1:56812 Closing
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60524 Accepted
+[Fri Jul 31 09:18:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60524 [200]: GET /api/stats.php
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60524 Closing
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60532 Accepted
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60532 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:18:31 2026] 127.0.0.1:60532 Closing
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34944 Accepted
+[Fri Jul 31 09:19:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34944 [200]: GET /api/stats.php
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34944 Closing
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34954 Accepted
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34954 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:19:31 2026] 127.0.0.1:34954 Closing
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57292 Accepted
+[Fri Jul 31 09:20:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57292 [200]: GET /api/stats.php
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57292 Closing
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57308 Accepted
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57308 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:20:31 2026] 127.0.0.1:57308 Closing
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53084 Accepted
+[Fri Jul 31 09:21:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53084 [200]: GET /api/stats.php
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53084 Closing
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53092 Accepted
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53092 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:21:31 2026] 127.0.0.1:53092 Closing
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50270 Accepted
+[Fri Jul 31 09:22:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50270 [200]: GET /api/stats.php
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50270 Closing
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50278 Accepted
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50278 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:22:31 2026] 127.0.0.1:50278 Closing
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50570 Accepted
+[Fri Jul 31 09:23:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50570 [200]: GET /api/stats.php
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50570 Closing
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50582 Accepted
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50582 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:23:31 2026] 127.0.0.1:50582 Closing
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37494 Accepted
+[Fri Jul 31 09:24:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37494 [200]: GET /api/stats.php
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37494 Closing
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37510 Accepted
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37510 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:24:31 2026] 127.0.0.1:37510 Closing
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36746 Accepted
+[Fri Jul 31 09:25:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36746 [200]: GET /api/stats.php
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36746 Closing
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36758 Accepted
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36758 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:25:31 2026] 127.0.0.1:36758 Closing
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:40998 Accepted
+[Fri Jul 31 09:26:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:40998 [200]: GET /api/stats.php
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:40998 Closing
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:41010 Accepted
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:41010 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:26:31 2026] 127.0.0.1:41010 Closing
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56788 Accepted
+[Fri Jul 31 09:27:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56788 [200]: GET /api/stats.php
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56788 Closing
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56800 Accepted
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56800 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:27:31 2026] 127.0.0.1:56800 Closing
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45876 Accepted
+[Fri Jul 31 09:28:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45876 [200]: GET /api/stats.php
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45876 Closing
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45880 Accepted
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45880 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:28:31 2026] 127.0.0.1:45880 Closing
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34066 Accepted
+[Fri Jul 31 09:29:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34066 [200]: GET /api/stats.php
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34066 Closing
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34070 Accepted
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34070 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:29:31 2026] 127.0.0.1:34070 Closing
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38216 Accepted
+[Fri Jul 31 09:30:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38216 [200]: GET /api/stats.php
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38216 Closing
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38222 Accepted
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38222 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:30:31 2026] 127.0.0.1:38222 Closing
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46234 Accepted
+[Fri Jul 31 09:31:31 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46234 [200]: GET /api/stats.php
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46234 Closing
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46250 Accepted
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46250 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:31:31 2026] 127.0.0.1:46250 Closing
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55392 Accepted
+[Fri Jul 31 09:31:45 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55392 [200]: GET /api/stats.php
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55392 Closing
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55400 Accepted
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55400 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:31:45 2026] 127.0.0.1:55400 Closing
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55414 Accepted
+[Fri Jul 31 09:31:50 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55414 [200]: GET /api/stats.php
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55414 Closing
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55420 Accepted
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55420 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:31:50 2026] 127.0.0.1:55420 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43562 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43562 [200]: GET /
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43562 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43570 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43570 [200]: GET /assets/index.css
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43570 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43582 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43582 [200]: GET /assets/nostr.bundle.js
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43590 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43594 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43582 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43590 [200]: GET /assets/nostr-lite.js
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43594 [200]: GET /assets/app.js
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43590 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43594 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43602 Accepted
+[Fri Jul 31 09:31:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43602 [200]: GET /api/stats.php
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43602 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43618 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43632 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43618 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43618 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43636 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43632 [200]: GET /favicon.ico
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43632 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43644 Accepted
+[Fri Jul 31 09:31:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43636 [200]: GET /api/stats.php
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43636 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43644 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43644 Closing
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43652 Accepted
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43652 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:31:58 2026] 127.0.0.1:43652 Closing
+[Fri Jul 31 09:32:00 2026] 127.0.0.1:43656 Accepted
+[Fri Jul 31 09:32:00 2026] 127.0.0.1:43656 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Fri Jul 31 09:32:00 2026] 127.0.0.1:43656 Closing
+[Fri Jul 31 09:32:03 2026] 127.0.0.1:43658 Accepted
+[Fri Jul 31 09:32:03 2026] 127.0.0.1:43658 [200]: GET /api/chart.php?range=day
+[Fri Jul 31 09:32:03 2026] 127.0.0.1:43658 Closing
+[Fri Jul 31 09:32:05 2026] 127.0.0.1:49930 Accepted
+[Fri Jul 31 09:32:05 2026] 127.0.0.1:49930 [200]: GET /api/chart.php?range=month
+[Fri Jul 31 09:32:05 2026] 127.0.0.1:49930 Closing
+[Fri Jul 31 09:32:06 2026] 127.0.0.1:49934 Accepted
+[Fri Jul 31 09:32:06 2026] 127.0.0.1:49934 [200]: GET /api/chart.php?range=year
+[Fri Jul 31 09:32:06 2026] 127.0.0.1:49934 Closing
+[Fri Jul 31 09:32:07 2026] 127.0.0.1:49948 Accepted
+[Fri Jul 31 09:32:07 2026] 127.0.0.1:49948 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:07 2026] 127.0.0.1:49948 Closing
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49962 Accepted
+[Fri Jul 31 09:32:08 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49962 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49962 Closing
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49970 Accepted
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49970 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:08 2026] 127.0.0.1:49970 Closing
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53498 Accepted
+[Fri Jul 31 09:32:18 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53498 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53498 Closing
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53504 Accepted
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53504 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:18 2026] 127.0.0.1:53504 Closing
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:32998 Accepted
+[Fri Jul 31 09:32:28 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:32998 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:32998 Closing
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:33008 Accepted
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:33008 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:28 2026] 127.0.0.1:33008 Closing
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37244 Accepted
+[Fri Jul 31 09:32:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37244 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37244 Closing
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37258 Accepted
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37258 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:38 2026] 127.0.0.1:37258 Closing
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52138 Accepted
+[Fri Jul 31 09:32:48 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52138 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52138 Closing
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52152 Accepted
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52152 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:48 2026] 127.0.0.1:52152 Closing
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57398 Accepted
+[Fri Jul 31 09:32:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57398 [200]: GET /api/stats.php
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57398 Closing
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57406 Accepted
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57406 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:32:58 2026] 127.0.0.1:57406 Closing
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36900 Accepted
+[Fri Jul 31 09:33:08 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36900 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36900 Closing
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36910 Accepted
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36910 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:08 2026] 127.0.0.1:36910 Closing
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59592 Accepted
+[Fri Jul 31 09:33:18 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59592 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59592 Closing
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59594 Accepted
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59594 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:18 2026] 127.0.0.1:59594 Closing
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55564 Accepted
+[Fri Jul 31 09:33:28 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55564 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55564 Closing
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55566 Accepted
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55566 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:28 2026] 127.0.0.1:55566 Closing
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57120 Accepted
+[Fri Jul 31 09:33:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57120 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57120 Closing
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57134 Accepted
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57134 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:38 2026] 127.0.0.1:57134 Closing
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34610 Accepted
+[Fri Jul 31 09:33:48 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34610 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34610 Closing
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34618 Accepted
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34618 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:48 2026] 127.0.0.1:34618 Closing
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40586 Accepted
+[Fri Jul 31 09:33:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40586 [200]: GET /api/stats.php
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40586 Closing
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40588 Accepted
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40588 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:33:58 2026] 127.0.0.1:40588 Closing
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41390 Accepted
+[Fri Jul 31 09:34:08 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41390 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41390 Closing
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41404 Accepted
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41404 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:08 2026] 127.0.0.1:41404 Closing
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54932 Accepted
+[Fri Jul 31 09:34:18 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54932 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54932 Closing
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54936 Accepted
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54936 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:18 2026] 127.0.0.1:54936 Closing
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55842 Accepted
+[Fri Jul 31 09:34:28 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55842 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55842 Closing
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55856 Accepted
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55856 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:28 2026] 127.0.0.1:55856 Closing
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36202 Accepted
+[Fri Jul 31 09:34:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36202 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36202 Closing
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36216 Accepted
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36216 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:38 2026] 127.0.0.1:36216 Closing
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44478 Accepted
+[Fri Jul 31 09:34:48 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44478 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44478 Closing
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44484 Accepted
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44484 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:48 2026] 127.0.0.1:44484 Closing
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47268 Accepted
+[Fri Jul 31 09:34:58 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47268 [200]: GET /api/stats.php
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47268 Closing
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47284 Accepted
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47284 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:34:58 2026] 127.0.0.1:47284 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47298 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47298 [200]: GET /
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47298 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47304 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47318 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47304 [200]: GET /assets/index.css
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47326 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47304 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47330 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47318 [200]: GET /assets/nostr.bundle.js
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47326 [200]: GET /assets/nostr-lite.js
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47330 [200]: GET /assets/app.js
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47330 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47318 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47326 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47340 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47344 Accepted
+[Fri Jul 31 09:35:00 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47340 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47340 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47344 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47344 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47356 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47372 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47356 [200]: GET /favicon.ico
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47356 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47380 Accepted
+[Fri Jul 31 09:35:00 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47372 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47372 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47380 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47380 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47388 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47388 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47388 Closing
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47396 Accepted
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47396 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Fri Jul 31 09:35:00 2026] 127.0.0.1:47396 Closing
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36514 Accepted
+[Fri Jul 31 09:35:10 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36514 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36514 Closing
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36526 Accepted
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36526 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:10 2026] 127.0.0.1:36526 Closing
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55696 Accepted
+[Fri Jul 31 09:35:20 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55696 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55696 Closing
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55708 Accepted
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55708 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:20 2026] 127.0.0.1:55708 Closing
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44562 Accepted
+[Fri Jul 31 09:35:30 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44562 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44562 Closing
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44564 Accepted
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44564 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:30 2026] 127.0.0.1:44564 Closing
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60422 Accepted
+[Fri Jul 31 09:35:40 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60422 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60422 Closing
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60434 Accepted
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60434 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:40 2026] 127.0.0.1:60434 Closing
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58448 Accepted
+[Fri Jul 31 09:35:50 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58448 [200]: GET /api/stats.php
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58448 Closing
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58454 Accepted
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58454 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:35:50 2026] 127.0.0.1:58454 Closing
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46774 Accepted
+[Fri Jul 31 09:36:00 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46774 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46774 Closing
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46778 Accepted
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46778 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:00 2026] 127.0.0.1:46778 Closing
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40158 Accepted
+[Fri Jul 31 09:36:10 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40158 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40158 Closing
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40162 Accepted
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40162 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:10 2026] 127.0.0.1:40162 Closing
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50762 Accepted
+[Fri Jul 31 09:36:20 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50762 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50762 Closing
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50764 Accepted
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50764 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:20 2026] 127.0.0.1:50764 Closing
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42246 Accepted
+[Fri Jul 31 09:36:30 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42246 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42246 Closing
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42260 Accepted
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42260 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:30 2026] 127.0.0.1:42260 Closing
+[Fri Jul 31 09:36:31 2026] 127.0.0.1:42266 Accepted
+[Fri Jul 31 09:36:31 2026] 127.0.0.1:42266 [200]: GET /api/chart.php?range=day
+[Fri Jul 31 09:36:31 2026] 127.0.0.1:42266 Closing
+[Fri Jul 31 09:36:32 2026] 127.0.0.1:42270 Accepted
+[Fri Jul 31 09:36:33 2026] 127.0.0.1:42270 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:33 2026] 127.0.0.1:42270 Closing
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46100 Accepted
+[Fri Jul 31 09:36:40 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46100 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46100 Closing
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46114 Accepted
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46114 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:40 2026] 127.0.0.1:46114 Closing
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53598 Accepted
+[Fri Jul 31 09:36:50 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53598 [200]: GET /api/stats.php
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53598 Closing
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53600 Accepted
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53600 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:36:50 2026] 127.0.0.1:53600 Closing
+[Fri Jul 31 09:36:57 2026] 127.0.0.1:35532 Accepted
+[Fri Jul 31 09:36:57 2026] 127.0.0.1:35532 [200]: GET /api/caching.php
+[Fri Jul 31 09:36:57 2026] 127.0.0.1:35532 Closing
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59884 Accepted
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59884 [200]: GET /api/caching.php
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59884 Closing
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59888 Accepted
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59888 [200]: POST /api/config.php
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59888 Closing
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59892 Accepted
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59892 [200]: GET /api/caching.php
+[Fri Jul 31 09:37:05 2026] 127.0.0.1:59892 Closing
+[Fri Jul 31 09:37:27 2026] 127.0.0.1:50106 Accepted
+[Fri Jul 31 09:37:27 2026] 127.0.0.1:50106 [200]: GET /api/events.php?limit=50
+[Fri Jul 31 09:37:27 2026] 127.0.0.1:50106 Closing
+[Fri Jul 31 09:37:34 2026] 127.0.0.1:50110 Accepted
+[Fri Jul 31 09:37:34 2026] 127.0.0.1:50110 [200]: GET /api/caching.php
+[Fri Jul 31 09:37:34 2026] 127.0.0.1:50110 Closing
+[Fri Jul 31 09:37:43 2026] 127.0.0.1:38120 Accepted
+[Fri Jul 31 09:37:43 2026] 127.0.0.1:38120 [200]: GET /api/caching.php
+[Fri Jul 31 09:37:43 2026] 127.0.0.1:38120 Closing
+[Fri Jul 31 09:38:32 2026] 127.0.0.1:42488 Accepted
+[Fri Jul 31 09:38:32 2026] 127.0.0.1:42488 [200]: GET /api/caching.php
+[Fri Jul 31 09:38:32 2026] 127.0.0.1:42488 Closing
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41006 Accepted
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41006 [200]: GET /assets/index.css
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41006 Closing
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41016 Accepted
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41016 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Fri Jul 31 09:38:40 2026] 127.0.0.1:41016 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58182 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58182 [200]: GET /
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58182 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58194 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58208 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58194 [200]: GET /assets/index.css
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58218 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58194 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58228 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58208 [200]: GET /assets/nostr.bundle.js
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58208 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58218 [200]: GET /assets/nostr-lite.js
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58218 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58228 [200]: GET /assets/app.js
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58228 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58240 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58240 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58240 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58256 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58260 Accepted
+[Fri Jul 31 09:41:49 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58256 [200]: GET /api/stats.php
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58256 Closing
+[Fri Jul 31 09:41:49 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58260 [200]: GET /api/stats.php
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58260 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58274 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58282 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58274 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58274 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58282 [200]: GET /favicon.ico
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58282 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58294 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58294 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58294 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58302 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58302 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58302 Closing
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58318 Accepted
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58318 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Fri Jul 31 09:41:49 2026] 127.0.0.1:58318 Closing
+[Fri Jul 31 09:41:54 2026] 127.0.0.1:58330 Accepted
+[Fri Jul 31 09:41:54 2026] 127.0.0.1:58330 [200]: GET /api/caching.php
+[Fri Jul 31 09:41:54 2026] 127.0.0.1:58330 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43738 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43738 [200]: GET /
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43738 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43754 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43760 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43754 [200]: GET /assets/index.css
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43776 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43760 [200]: GET /assets/nostr.bundle.js
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43782 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43776 [200]: GET /assets/nostr-lite.js
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43754 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43782 [200]: GET /assets/app.js
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43760 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43776 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43782 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43786 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43786 [200]: GET /.well-known/appspecific/com.chrome.devtools.json
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43786 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43800 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43804 Accepted
+[Fri Jul 31 09:42:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43800 [200]: GET /api/stats.php
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43800 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43814 Accepted
+[Fri Jul 31 09:42:38 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43804 [200]: GET /api/stats.php
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43804 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43828 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43814 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43814 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43828 [200]: GET /favicon.ico
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43828 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43842 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43842 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43842 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43858 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43858 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43858 Closing
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43868 Accepted
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43868 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Fri Jul 31 09:42:38 2026] 127.0.0.1:43868 Closing
+[Fri Jul 31 09:42:43 2026] 127.0.0.1:43884 Accepted
+[Fri Jul 31 09:42:43 2026] 127.0.0.1:43884 [200]: GET /api/caching.php
+[Fri Jul 31 09:42:43 2026] 127.0.0.1:43884 Closing
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44536 Accepted
+[Fri Jul 31 09:44:23 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44536 [200]: GET /api/stats.php
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44536 Closing
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44552 Accepted
+[Fri Jul 31 09:44:23 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44552 [200]: GET /api/stats.php
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44552 Closing
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44562 Accepted
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44562 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44562 Closing
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44566 Accepted
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44566 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:44:23 2026] 127.0.0.1:44566 Closing
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58404 Accepted
+[Fri Jul 31 09:44:33 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58404 [200]: GET /api/stats.php
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58404 Closing
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58420 Accepted
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58420 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:44:33 2026] 127.0.0.1:58420 Closing
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35644 Accepted
+[Fri Jul 31 09:44:43 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35644 [200]: GET /api/stats.php
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35644 Closing
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35646 Accepted
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35646 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:44:43 2026] 127.0.0.1:35646 Closing
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60230 Accepted
+[Fri Jul 31 09:44:53 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60230 [200]: GET /api/stats.php
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60230 Closing
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60236 Accepted
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60236 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:44:53 2026] 127.0.0.1:60236 Closing
+[Fri Jul 31 09:45:01 2026] 127.0.0.1:43410 Accepted
+[Fri Jul 31 09:45:01 2026] 127.0.0.1:43410 [200]: GET /api/caching.php
+[Fri Jul 31 09:45:01 2026] 127.0.0.1:43410 Closing
+[Fri Jul 31 09:45:46 2026] 127.0.0.1:36570 Accepted
+[Fri Jul 31 09:45:46 2026] 127.0.0.1:36570 [200]: GET /api/caching.php
+[Fri Jul 31 09:45:46 2026] 127.0.0.1:36570 Closing
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36582 Accepted
+[Fri Jul 31 09:45:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36582 [200]: GET /api/stats.php
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36582 Closing
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36598 Accepted
+[Fri Jul 31 09:45:51 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36598 [200]: GET /api/stats.php
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36598 Closing
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36612 Accepted
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36612 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36612 Closing
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36614 Accepted
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36614 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:45:51 2026] 127.0.0.1:36614 Closing
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37848 Accepted
+[Fri Jul 31 09:46:01 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37848 [200]: GET /api/stats.php
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37848 Closing
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37850 Accepted
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37850 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 09:46:01 2026] 127.0.0.1:37850 Closing
+[Fri Jul 31 09:46:10 2026] 127.0.0.1:57520 Accepted
+[Fri Jul 31 09:46:10 2026] 127.0.0.1:57520 [200]: GET /api/caching.php
+[Fri Jul 31 09:46:10 2026] 127.0.0.1:57520 Closing
+[Fri Jul 31 10:01:55 2026] 127.0.0.1:40744 Accepted
+[Fri Jul 31 10:01:55 2026] 127.0.0.1:40744 [200]: POST /api/caching.php
+[Fri Jul 31 10:01:55 2026] 127.0.0.1:40744 Closing
+[Fri Jul 31 10:02:49 2026] 127.0.0.1:47800 Accepted
+[Fri Jul 31 10:02:49 2026] 127.0.0.1:47800 [200]: GET /api/caching.php
+[Fri Jul 31 10:02:49 2026] 127.0.0.1:47800 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38430 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38430 [200]: GET /
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38430 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38434 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38434 [200]: GET /assets/index.css
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38434 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38448 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38464 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38474 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38448 [200]: GET /assets/nostr.bundle.js
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38464 [200]: GET /assets/nostr-lite.js
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38474 [200]: GET /assets/app.js
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38448 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38464 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38474 Closing
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38476 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38478 Accepted
+[Fri Jul 31 10:04:58 2026] 127.0.0.1:38486 Accepted
+[Fri Jul 31 10:04:59 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38476 [200]: GET /api/stats.php
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38476 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38490 Accepted
+[Fri Jul 31 10:04:59 2026] PHP Warning: syntax error, unexpected '(' in /proc/meminfo on line 9
+ in /home/user/lt/c-relay-pg/admin/api/stats.php on line 47
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38478 [200]: GET /api/stats.php
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38478 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38486 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38486 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38490 [200]: GET /favicon.ico
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38490 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38500 Accepted
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38502 Accepted
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38500 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38500 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38502 [200]: GET /api/profile.php?pubkey=8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38502 Closing
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38506 Accepted
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38506 [200]: GET /api/chart.php?range=hour
+[Fri Jul 31 10:04:59 2026] 127.0.0.1:38506 Closing
+[Fri Jul 31 10:05:03 2026] 127.0.0.1:38522 Accepted
+[Fri Jul 31 10:05:03 2026] 127.0.0.1:38522 [200]: GET /api/caching.php
+[Fri Jul 31 10:05:03 2026] 127.0.0.1:38522 Closing
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56336 Accepted
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56336 [200]: POST /api/caching.php
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56336 Closing
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56348 Accepted
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56348 [200]: GET /api/caching.php
+[Fri Jul 31 10:05:21 2026] 127.0.0.1:56348 Closing
+[Fri Jul 31 10:07:01 2026] 127.0.0.1:33980 Accepted
+[Fri Jul 31 10:07:01 2026] 127.0.0.1:33980 [200]: GET /api/caching.php
+[Fri Jul 31 10:07:01 2026] 127.0.0.1:33980 Closing
diff --git a/admin/relays.php b/admin/relays.php
deleted file mode 100644
index 033cc71..0000000
--- a/admin/relays.php
+++ /dev/null
@@ -1,132 +0,0 @@
-prepare("SELECT COUNT(*) FROM caching_backfill_relay_progress rp $where_sql");
- $stmt->execute($params);
- $total = intval($stmt->fetchColumn());
-
- $sql = "
- SELECT rp.author_pubkey, rp.relay_url, rp.until_cursor, rp.complete,
- rp.events_fetched,
- COALESCE(rp.consecutive_errors, 0) AS consecutive_errors,
- rp.last_status, rp.updated_at,
- e.content::json->>'name' AS name,
- e.content::json->>'display_name' AS display_name
- FROM caching_backfill_relay_progress rp
- LEFT JOIN LATERAL (SELECT content FROM events WHERE pubkey = rp.author_pubkey AND kind = 0 ORDER BY created_at DESC LIMIT 1) e ON true
- $where_sql
- ORDER BY rp.complete ASC, COALESCE(rp.consecutive_errors, 0) DESC, rp.updated_at DESC
- LIMIT $per OFFSET $offset
- ";
- $stmt = $pdo->prepare($sql);
- $stmt->execute($params);
- $relays = $stmt->fetchAll();
-} catch (PDOException $ex) {}
-
-function status_badge(string $status): string {
- if ($status === 'eose') return 'eose ';
- if ($status === 'timeout') return 'timeout ';
- if (str_starts_with($status, 'error')) return '' . e($status) . ' ';
- if (str_starts_with($status, 'NOTICE')) return '' . e($status) . ' ';
- if (str_starts_with($status, 'CLOSED')) return '' . e($status) . ' ';
- if ($status === '') return '— ';
- return '' . e($status) . ' ';
-}
-
-admin_header('relays', 'C-Relay-PG Admin — Relay Progress');
-?>
-
-
-
-
-
-
-
-
-
-
- Author
- Relay URL
- Complete
- Events
- Errors
- Last Status
- Updated
-
-
-
-
- = 3 ? 'style="background:rgba(255,0,0,0.05)"' : '' ?>>
-
-
- = e($name) ?>
-
- unknown
-
- = e(trunc($npub, 20)) ?>
-
- = e($r['relay_url']) ?>
- = $r['complete'] ? '✓ ' : '… ' ?>
- = number_format(intval($r['events_fetched'])) ?>
- = intval($r['consecutive_errors']) > 0
- ? '' . intval($r['consecutive_errors']) . ' '
- : '0' ?>
- = status_badge($r['last_status']) ?>
- = time_ago(intval($r['updated_at'])) ?>
-
-
-
- No relay progress rows found.
-
-
-
-
-
- = pagination($page, $per, $total, 'relays.php' . ($filter !== 'all' ? '?filter=' . e($filter) : '') . ($pubkey ? '&pubkey=' . e($pubkey) : '')) ?>
-
-
-
diff --git a/admin/serve.sh b/admin/serve.sh
new file mode 100755
index 0000000..b96d4d1
--- /dev/null
+++ b/admin/serve.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Launch the admin2 PHP built-in server, fully detached from the calling shell.
+# Usage: ./admin2/serve.sh [port] (default port 8088)
+PORT="${1:-8088}"
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+# Kill any existing instance on this port.
+pkill -f "php -S 127.0.0.1:$PORT" 2>/dev/null
+sleep 1
+
+# Start fully detached in its own session so SIGTERM to the launcher
+# does not propagate to the PHP server.
+setsid php -S 127.0.0.1:$PORT -t "$SCRIPT_DIR" > "$SCRIPT_DIR/php_server.log" 2>&1 < /dev/null &
+PHP_PID=$!
+disown $PHP_PID 2>/dev/null
+
+# Give it a moment to bind, then report.
+sleep 1
+if kill -0 "$PHP_PID" 2>/dev/null; then
+ echo "admin PHP server started on http://127.0.0.1:$PORT (PID $PHP_PID)"
+ echo "Serving from: $SCRIPT_DIR"
+ echo "Log: $SCRIPT_DIR/php_server.log"
+else
+ echo "ERROR: PHP server failed to start. Log:"
+ cat "$SCRIPT_DIR/php_server.log" 2>/dev/null
+ exit 1
+fi
diff --git a/caching/Makefile b/caching/Makefile
index 566f37c..c792378 100644
--- a/caching/Makefile
+++ b/caching/Makefile
@@ -22,7 +22,7 @@ LIBS = -lwebsockets -lssl -lcrypto -lsecp256k1 -lcurl -lz -ldl -lpthread -lm -lp
MAIN_SRC = src/main.c src/debug.c src/jsonc_strip.c src/config.c src/state.c \
src/follow_graph.c src/relay_sink.c src/live_subscriber.c \
- src/backfill.c src/relay_discovery.c \
+ src/backfill.c src/forward_catchup.c src/relay_discovery.c \
src/pg_inbox.c src/pg_config.c
# Architecture detection
diff --git a/caching/src/backfill.c b/caching/src/backfill.c
index 92043b6..198b241 100644
--- a/caching/src/backfill.c
+++ b/caching/src/backfill.c
@@ -134,6 +134,24 @@ static int find_oldest_created_at(cJSON **events, int count, long *out_oldest) {
return found;
}
+/* Find the newest (maximum) created_at among an array of event JSON objects.
+ * Returns 1 and sets *out_newest, or 0 if no events / no valid created_at. */
+static int find_newest_created_at(cJSON **events, int count, long *out_newest) {
+ long newest = 0;
+ int found = 0;
+ for (int i = 0; i < count; i++) {
+ cJSON *ca = cJSON_GetObjectItem(events[i], "created_at");
+ if (!ca || !cJSON_IsNumber(ca)) continue;
+ long ts = (long)ca->valuedouble;
+ if (!found || ts > newest) {
+ newest = ts;
+ found = 1;
+ }
+ }
+ if (found && out_newest) *out_newest = newest;
+ return found;
+}
+
/* Build the kinds array for a given pubkey (admin vs regular).
* Returns NULL if no kinds filter should be applied (admin_all_kinds). */
static cJSON *build_kinds(cr_config_t *cfg, const char *pk) {
@@ -370,9 +388,11 @@ int cr_backfill_tick(cr_backfill_t *bf, cr_config_t *cfg,
continue;
}
- /* Find oldest timestamp, then publish every event. */
+ /* Find oldest and newest timestamps, then publish every event. */
long oldest_ts = 0;
+ long newest_ts = 0;
find_oldest_created_at(events, ev_count, &oldest_ts);
+ find_newest_created_at(events, ev_count, &newest_ts);
for (int k = 0; k < ev_count; k++) {
cr_sink_publish(sink, events[k]);
@@ -381,6 +401,11 @@ int cr_backfill_tick(cr_backfill_t *bf, cr_config_t *cfg,
free(events);
bf->events_total += ev_count;
+ /* Update last_event_at for forward catch-up gap tracking. */
+ if (newest_ts > 0) {
+ pg_inbox_update_last_event_at(pk, newest_ts);
+ }
+
DEBUG_LOG("backfill: %s @ %s -> %d events (until=%ld, oldest=%ld, eose=%d)",
pk, relay_url, ev_count, until_cursor, oldest_ts, qctx.got_eose);
diff --git a/caching/src/forward_catchup.c b/caching/src/forward_catchup.c
new file mode 100644
index 0000000..f03605d
--- /dev/null
+++ b/caching/src/forward_catchup.c
@@ -0,0 +1,199 @@
+/*
+ * caching_relay - forward catch-up implementation.
+ *
+ * See forward_catchup.h for design.
+ */
+#include "forward_catchup.h"
+#include "pg_inbox.h"
+#include "config.h"
+#include "relay_sink.h"
+#include "debug.h"
+
+#include
+#include
+#include
+#include
+
+/* Find the newest (maximum) created_at among an array of event JSON objects.
+ * Returns 1 and sets *out_newest, or 0 if no events / no valid created_at. */
+static int find_newest_created_at(cJSON **events, int count, long *out_newest) {
+ long newest = 0;
+ int found = 0;
+ for (int i = 0; i < count; i++) {
+ cJSON *ca = cJSON_GetObjectItem(events[i], "created_at");
+ if (!ca || !cJSON_IsNumber(ca)) continue;
+ long ts = (long)ca->valuedouble;
+ if (!found || ts > newest) {
+ newest = ts;
+ found = 1;
+ }
+ }
+ if (found && out_newest) *out_newest = newest;
+ return found;
+}
+
+/* Build the kinds array for a given pubkey (admin vs regular).
+ * Returns NULL if no kinds filter should be applied (admin_all_kinds). */
+static cJSON *build_kinds(cr_config_t *cfg, const char *pk) {
+ int is_admin = cr_follow_is_root(cfg, pk);
+ if (is_admin && cfg->admin_all_kinds) {
+ return NULL;
+ }
+ cJSON *kinds = cJSON_CreateArray();
+ if (is_admin && cfg->admin_kind_count > 0) {
+ for (int i = 0; i < cfg->admin_kind_count; i++)
+ cJSON_AddItemToArray(kinds, cJSON_CreateNumber(cfg->admin_kinds[i]));
+ } else {
+ for (int i = 0; i < cfg->kind_count; i++)
+ cJSON_AddItemToArray(kinds, cJSON_CreateNumber(cfg->kinds[i]));
+ }
+ return kinds;
+}
+
+/* Get relay URLs for an author from the relay progress table.
+ * Returns a cJSON array of relay_url strings. Caller must cJSON_Delete().
+ * Returns NULL on error or if no relays found. */
+static cJSON *get_author_relays(const char *pk) {
+ /* Use pg_inbox_get_incomplete_relays for incomplete authors.
+ * For completed authors, we need all relays — query directly. */
+ cJSON *incomplete = pg_inbox_get_incomplete_relays(pk);
+ if (incomplete && cJSON_GetArraySize(incomplete) > 0) {
+ /* Extract just the relay_url strings. */
+ cJSON *urls = cJSON_CreateArray();
+ int n = cJSON_GetArraySize(incomplete);
+ for (int i = 0; i < n; i++) {
+ cJSON *entry = cJSON_GetArrayItem(incomplete, i);
+ cJSON *url = cJSON_GetObjectItemCaseSensitive(entry, "relay_url");
+ if (url && cJSON_IsString(url) && url->valuestring[0]) {
+ cJSON_AddItemToArray(urls, cJSON_CreateString(url->valuestring));
+ }
+ }
+ cJSON_Delete(incomplete);
+ if (cJSON_GetArraySize(urls) > 0) return urls;
+ cJSON_Delete(urls);
+ }
+ if (incomplete) cJSON_Delete(incomplete);
+
+ /* For completed authors, fall back to the upstream pool's connected
+ * relays. The caller will use the pool directly. */
+ return NULL;
+}
+
+int cr_forward_catchup(cr_config_t *cfg,
+ nostr_relay_pool_t *upstream,
+ cr_sink_t *sink) {
+ if (!cfg || !upstream || !sink) return -1;
+
+ cJSON *authors = pg_inbox_get_authors_for_catchup();
+ if (!authors) {
+ DEBUG_INFO("forward_catchup: no authors with last_event_at > 0");
+ return 0;
+ }
+
+ int author_count = cJSON_GetArraySize(authors);
+ if (author_count == 0) {
+ cJSON_Delete(authors);
+ DEBUG_INFO("forward_catchup: no authors need catch-up");
+ return 0;
+ }
+
+ DEBUG_INFO("forward_catchup: checking %d authors for missed events", author_count);
+
+ long now = (long)time(NULL);
+ int page_size = cfg->backfill.events_per_tick;
+ if (page_size < 1) page_size = 500;
+
+ cr_sink_set_source_class(sink, CR_SINK_CLASS_BACKFILL);
+
+ int total_events_published = 0;
+ int authors_caught_up = 0;
+
+ for (int i = 0; i < author_count; i++) {
+ cJSON *entry = cJSON_GetArrayItem(authors, i);
+ if (!entry) continue;
+
+ cJSON *pk_node = cJSON_GetObjectItemCaseSensitive(entry, "pubkey");
+ cJSON *lea_node = cJSON_GetObjectItemCaseSensitive(entry, "last_event_at");
+ if (!pk_node || !cJSON_IsString(pk_node) || !lea_node || !cJSON_IsNumber(lea_node))
+ continue;
+
+ const char *pk = pk_node->valuestring;
+ long last_event_at = (long)lea_node->valuedouble;
+
+ if (last_event_at <= 0 || last_event_at >= now) continue;
+
+ /* Build filter: since = last_event_at + 1, until = now. */
+ cJSON *filter = cJSON_CreateObject();
+ cJSON *authors_arr = cJSON_CreateArray();
+ cJSON_AddItemToArray(authors_arr, cJSON_CreateString(pk));
+ cJSON_AddItemToObject(filter, "authors", authors_arr);
+ cJSON *kinds = build_kinds(cfg, pk);
+ if (kinds) cJSON_AddItemToObject(filter, "kinds", kinds);
+ cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber((double)(last_event_at + 1)));
+ cJSON_AddItemToObject(filter, "until", cJSON_CreateNumber((double)now));
+ cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber((double)page_size));
+
+ /* Try to get author-specific relays first; fall back to upstream pool. */
+ cJSON *author_relays = get_author_relays(pk);
+
+ int ev_count = 0;
+ cJSON **events = NULL;
+
+ if (author_relays && cJSON_GetArraySize(author_relays) > 0) {
+ /* Query using the author's outbox relays. */
+ int n_relays = cJSON_GetArraySize(author_relays);
+ const char **urls = malloc(sizeof(char *) * n_relays);
+ for (int r = 0; r < n_relays; r++) {
+ cJSON *u = cJSON_GetArrayItem(author_relays, r);
+ urls[r] = cJSON_IsString(u) ? u->valuestring : "";
+ }
+ events = synchronous_query_relays_with_progress(
+ urls, n_relays, filter, RELAY_QUERY_ALL_RESULTS,
+ &ev_count, 30, NULL, NULL, 0, NULL);
+ free(urls);
+ } else {
+ /* Fall back: query all connected upstream relays.
+ * Use the pool's relay list. We pass NULL for urls to let
+ * the pool use all connected relays. */
+ events = synchronous_query_relays_with_progress(
+ NULL, 0, filter, RELAY_QUERY_ALL_RESULTS,
+ &ev_count, 30, NULL, NULL, 0, NULL);
+ }
+ cJSON_Delete(author_relays);
+ cJSON_Delete(filter);
+
+ if (events && ev_count > 0) {
+ /* Publish all events. */
+ long newest = 0;
+ find_newest_created_at(events, ev_count, &newest);
+
+ for (int k = 0; k < ev_count; k++) {
+ cr_sink_publish(sink, events[k]);
+ cJSON_Delete(events[k]);
+ }
+ free(events);
+ total_events_published += ev_count;
+ authors_caught_up++;
+
+ /* Update last_event_at to the newest event we saw. */
+ if (newest > 0) {
+ pg_inbox_update_last_event_at(pk, newest);
+ }
+
+ DEBUG_LOG("forward_catchup: %s -> %d events (since=%ld, until=%ld)",
+ pk, ev_count, last_event_at + 1, now);
+ } else {
+ /* No events in the gap — update last_event_at to now so we
+ * don't re-query the same empty window next time. */
+ pg_inbox_update_last_event_at(pk, now);
+ if (events) free(events);
+ }
+ }
+
+ cJSON_Delete(authors);
+
+ DEBUG_INFO("forward_catchup: %d authors checked, %d had events, %d total events published",
+ author_count, authors_caught_up, total_events_published);
+
+ return 0;
+}
diff --git a/caching/src/forward_catchup.h b/caching/src/forward_catchup.h
new file mode 100644
index 0000000..ae9cfd0
--- /dev/null
+++ b/caching/src/forward_catchup.h
@@ -0,0 +1,30 @@
+/*
+ * caching_relay - forward catch-up for gap bridging.
+ *
+ * When the caching service starts (or caching is re-enabled after being
+ * off), events posted by followed authors during the downtime are missed
+ * by the backward-drain backfill (which walks toward the beginning of
+ * time) and by the live subscriber (which starts at `since = now`).
+ *
+ * The forward catch-up queries `since = last_event_at + 1, until = now`
+ * for each followed author to bridge that gap. `last_event_at` is the
+ * `created_at` of the most recent event we've ever seen for the author,
+ * maintained by the backfill and live subscriber.
+ */
+#ifndef CACHING_RELAY_FORWARD_CATCHUP_H
+#define CACHING_RELAY_FORWARD_CATCHUP_H
+
+#include "config.h"
+#include "relay_sink.h"
+#include "../nostr_core_lib/nostr_core/nostr_core.h"
+
+/* Run forward catch-up for all followed authors with last_event_at > 0.
+ * For each author, queries events from last_event_at + 1 to now on one
+ * outbox relay and publishes them to the sink.
+ *
+ * Returns 0 on success, -1 on error. */
+int cr_forward_catchup(cr_config_t *cfg,
+ nostr_relay_pool_t *upstream,
+ cr_sink_t *sink);
+
+#endif /* CACHING_RELAY_FORWARD_CATCHUP_H */
diff --git a/caching/src/live_subscriber.c b/caching/src/live_subscriber.c
index 16b7568..7e57849 100644
--- a/caching/src/live_subscriber.c
+++ b/caching/src/live_subscriber.c
@@ -4,6 +4,7 @@
#define _GNU_SOURCE
#include "live_subscriber.h"
#include "follow_graph.h"
+#include "pg_inbox.h"
#include "debug.h"
#include
@@ -25,6 +26,15 @@ static void live_on_event(cJSON *event, const char *relay_url, void *user_data)
ctx->live->events_received++;
cr_sink_publish(ctx->sink, event);
+ /* Update last_event_at for forward catch-up gap tracking. */
+ cJSON *ev_pubkey = cJSON_GetObjectItem(event, "pubkey");
+ cJSON *ev_created = cJSON_GetObjectItem(event, "created_at");
+ if (ev_pubkey && cJSON_IsString(ev_pubkey) &&
+ ev_created && cJSON_IsNumber(ev_created)) {
+ pg_inbox_update_last_event_at(ev_pubkey->valuestring,
+ (long)ev_created->valuedouble);
+ }
+
/* Detect admin kind-3 (contact list) changes from a root pubkey and
* signal the main loop to refresh the follow graph immediately. */
if (ctx->cfg) {
diff --git a/caching/src/main.c b/caching/src/main.c
index 0034cb3..6cb7641 100644
--- a/caching/src/main.c
+++ b/caching/src/main.c
@@ -17,6 +17,7 @@
#include "relay_sink.h"
#include "live_subscriber.h"
#include "backfill.h"
+#include "forward_catchup.h"
#include "relay_discovery.h"
#include "pg_inbox.h"
#include "pg_config.h"
@@ -367,6 +368,15 @@ int main(int argc, char **argv) {
init_relay_progress_for_all(&cfg, &relay_map, &followed);
}
+ /* Forward catch-up: bridge the gap for events posted while caching
+ * was off. Runs once at startup (not on --restart, which does a full
+ * re-drain from now). For each followed author with last_event_at > 0,
+ * queries since = last_event_at + 1, until = now. */
+ if (pg_conn && !restart && cfg.backfill.enabled) {
+ DEBUG_INFO("forward catch-up: bridging gap for followed authors");
+ cr_forward_catchup(&cfg, upstream, &sink);
+ }
+
/* Open live subscription. */
cr_live_t live;
if (cfg.live.enabled) {
diff --git a/caching/src/pg_inbox.c b/caching/src/pg_inbox.c
index 596470d..81d0cb3 100644
--- a/caching/src/pg_inbox.c
+++ b/caching/src/pg_inbox.c
@@ -1100,3 +1100,89 @@ int pg_inbox_get_active_target(char *out_pubkey, int pk_len,
PQclear(res);
return 0;
}
+
+/* ------------------------------------------------------------------ */
+/* Forward catch-up support (last_event_at tracking) */
+/* ------------------------------------------------------------------ */
+
+cJSON* pg_inbox_get_authors_for_catchup(void) {
+ if (!g_pg) {
+ DEBUG_ERROR("pg_inbox: get_authors_for_catchup: not initialized");
+ return NULL;
+ }
+
+ const char *sql =
+ "SELECT pubkey, last_event_at "
+ " FROM caching_followed_pubkeys "
+ " WHERE last_event_at > 0 "
+ " ORDER BY last_event_at ASC";
+
+ PGresult *res = PQexec(g_pg, sql);
+ if (!res) {
+ DEBUG_ERROR("pg_inbox: get_authors_for_catchup: NULL result");
+ return NULL;
+ }
+ ExecStatusType st = PQresultStatus(res);
+ if (st != PGRES_TUPLES_OK) {
+ DEBUG_ERROR("pg_inbox: get_authors_for_catchup failed: %s",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ return NULL;
+ }
+
+ cJSON *arr = cJSON_CreateArray();
+ if (!arr) {
+ PQclear(res);
+ return NULL;
+ }
+
+ int n = PQntuples(res);
+ for (int i = 0; i < n; i++) {
+ const char *pk = PQgetvalue(res, i, 0);
+ long last_event_at = strtol(PQgetvalue(res, i, 1), NULL, 10);
+ if (!pk || pk[0] == '\0') continue;
+
+ cJSON *obj = cJSON_CreateObject();
+ cJSON_AddStringToObject(obj, "pubkey", pk);
+ cJSON_AddNumberToObject(obj, "last_event_at", (double)last_event_at);
+ cJSON_AddItemToArray(arr, obj);
+ }
+
+ PQclear(res);
+ return arr;
+}
+
+int pg_inbox_update_last_event_at(const char *pk, long event_created_at) {
+ if (!g_pg) {
+ DEBUG_ERROR("pg_inbox: update_last_event_at: not initialized");
+ return -1;
+ }
+ if (!pk || pk[0] == '\0' || event_created_at <= 0) return -1;
+
+ char ea_buf[32];
+ snprintf(ea_buf, sizeof(ea_buf), "%ld", event_created_at);
+
+ const char *vals[2] = {pk, ea_buf};
+ int lens[2] = {(int)strlen(pk), (int)strlen(ea_buf)};
+ int fmts[2] = {0, 0};
+
+ const char *sql =
+ "UPDATE caching_followed_pubkeys "
+ " SET last_event_at = GREATEST(last_event_at, $2::BIGINT) "
+ " WHERE pubkey = $1";
+
+ PGresult *res = PQexecParams(g_pg, sql, 2, NULL, vals, lens, fmts, 0);
+ if (!res) {
+ DEBUG_ERROR("pg_inbox: update_last_event_at: NULL result");
+ return -1;
+ }
+ ExecStatusType st = PQresultStatus(res);
+ if (st != PGRES_COMMAND_OK) {
+ DEBUG_ERROR("pg_inbox: update_last_event_at failed: %s",
+ PQresultErrorMessage(res));
+ PQclear(res);
+ return -1;
+ }
+ PQclear(res);
+ return 0;
+}
diff --git a/caching/src/pg_inbox.h b/caching/src/pg_inbox.h
index 20bbb20..cca9745 100644
--- a/caching/src/pg_inbox.h
+++ b/caching/src/pg_inbox.h
@@ -149,4 +149,17 @@ int pg_inbox_set_active_target(const char *pubkey, const char *relay_url);
int pg_inbox_get_active_target(char *out_pubkey, int pk_len,
char *out_relay, int relay_len);
+/* ------------------------------------------------------------------ */
+/* Forward catch-up support (last_event_at tracking) */
+/* ------------------------------------------------------------------ */
+
+/* Returns a cJSON array of {pubkey, last_event_at} objects for all
+ * followed authors where last_event_at > 0. Caller must cJSON_Delete().
+ * Returns NULL on error. */
+cJSON* pg_inbox_get_authors_for_catchup(void);
+
+/* Update last_event_at for a pubkey to the GREATEST of current and
+ * the given value. Returns 0 on success, -1 on error. */
+int pg_inbox_update_last_event_at(const char *pk, long event_created_at);
+
#endif /* CACHING_RELAY_PG_INBOX_H */
diff --git a/plans/admin2_php_full_admin_plan.md b/plans/admin2_php_full_admin_plan.md
new file mode 100644
index 0000000..3bcb9c8
--- /dev/null
+++ b/plans/admin2_php_full_admin_plan.md
@@ -0,0 +1,206 @@
+# admin2: Full PHP Admin for C-Relay-PG
+
+## Goal
+
+Recreate the entire c-relay-pg admin interface as a PHP application in a new
+`admin2/` directory, starting from the existing `api/` HTML/CSS/JS files and
+replacing the Nostr admin command backend with direct PostgreSQL queries via
+PHP PDO. This covers ALL admin sections, not just caching.
+
+## Architecture
+
+Same as `admin/` (PHP + nginx + PDO + 10s polling), but with the full
+section set from the original `api/index.html`:
+
+```mermaid
+flowchart LR
+ Browser -->|HTTPS /admin2/| Nginx
+ Nginx -->|*.php| PHP_FPM
+ Nginx -->|/relay/| Relay[C-Relay-PG]
+ PHP_FPM -->|PDO| PostgreSQL
+ Relay -->|libpq| PostgreSQL
+```
+
+## Sections (9 pages, matching original nav)
+
+| # | Nav Label | Section ID | Data Source | PHP API Endpoint |
+|---|-----------|------------|-------------|------------------|
+| 1 | Statistics | databaseStatisticsSection | `pg_database_size()`, `events` count, `pg_stat_activity`, process stats | `api/stats.php` |
+| 2 | Subscriptions | subscriptionDetailsSection | `subscriptions` + `subscription_metrics` tables | `api/subscriptions.php` |
+| 3 | Configuration | div_config | `config` table (read/edit all keys) | `api/config.php` |
+| 4 | Authorization | authRulesSection + wotSection | `auth_rules` table | `api/auth.php` |
+| 5 | IP BAN | ipBansSection | `ip_bans` table | `api/ipbans.php` |
+| 6 | Relay Events | relayEventsSection | `events` table (recent events, kind filter) | `api/events.php` |
+| 7 | Caching | cachingSection | `caching_*` tables (already built in `admin/`) | `api/caching.php` |
+| 8 | DM | nip17DMSection | `events` table (kind 4/14/15 DMs) | `api/dm.php` |
+| 9 | Database Query | sqlQuerySection | Direct SQL execution (admin only) | `api/query.php` |
+
+## Graph on Statistics Page
+
+The original has an event-rate chart. For the PHP version:
+- **X-axis:** one tick = 10 seconds (the refresh interval)
+- **Y-axis:** events per interval
+- **Data source:** `api/stats.php` returns `events_delta` (count of new events since last poll)
+- **JS:** maintains a rolling array of the last 60 data points (10 minutes of history), appends a new point on each 10s refresh, renders as a simple canvas/SVG line chart
+- **No external chart library** — use a lightweight inline canvas renderer to keep it dependency-free
+
+## File Structure
+
+```
+admin2/
+├── index.php ← Single-page app (all 9 sections in one page, show/hide via JS)
+├── assets/
+│ ├── index.css ← Copied from api/index.css (unchanged)
+│ └── app.js ← Adapted from api/index.js (replace Nostr commands with fetch() to PHP APIs)
+├── lib/
+│ ├── config.php ← DB connection config (same as admin/)
+│ ├── db.php ← PDO helper (same as admin/)
+│ └── helpers.php ← Shared helpers (same as admin/)
+├── api/
+│ ├── stats.php ← Statistics: DB size, event counts, process info, event-rate delta
+│ ├── subscriptions.php ← Subscription details + metrics
+│ ├── config.php ← Config table read/edit (all keys, not just caching)
+│ ├── auth.php ← Auth rules + WoT status
+│ ├── ipbans.php ← IP ban list (read/add/remove)
+│ ├── events.php ← Recent relay events (paginated, kind filter)
+│ ├── caching.php ← Caching status (reuses admin/api/status.php logic)
+│ ├── dm.php ← DM list (kind 4/14/15 events)
+│ └── query.php ← Direct SQL query execution (admin only, read-only by default)
+└── README.md
+```
+
+## Implementation Plan
+
+### Step 1 — Scaffold admin2/ directory
+- Create `admin2/` with `lib/` (copy from `admin/`), `assets/`, `api/`
+- Copy `api/index.css` → `admin2/assets/index.css` (unchanged)
+- Copy `api/index.html` → `admin2/index.php` (will be adapted)
+
+### Step 2 — Adapt index.php from api/index.html
+- Remove Nostr login/auth (NIP-07 extension, kind-23456 admin commands)
+- Remove WebSocket connection logic (replaced by PHP AJAX polling)
+- Keep all 9 section HTML structures exactly as-is
+- Keep the side-nav, header, section show/hide JS
+- Replace `sendAdminCommand()` calls with `fetch('api/*.php')` calls
+- Add 10s auto-refresh via `setInterval` for statistics page
+- Add event-rate chart canvas renderer
+
+### Step 3 — Adapt app.js from api/index.js
+- Keep: `switchPage()`, nav handling, section show/hide, config table rendering
+- Remove: WebSocket connection, NIP-07 login, NIP-44 encryption, kind-23456 event sending
+- Replace: each `sendAdminCommand(['system_command', '...'])` with `fetch('api/....php')`
+- Add: `refreshStats()` function that polls `api/stats.php` every 10s
+- Add: event-rate chart rendering on canvas (rolling 60-point window)
+
+### Step 4 — Build PHP API endpoints (9 files)
+Each endpoint returns JSON, queried directly from PostgreSQL via PDO:
+
+**api/stats.php** — Statistics page data:
+```sql
+SELECT pg_database_size(current_database()) AS db_size;
+SELECT COUNT(*) FROM events;
+SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active';
+-- Event rate: count events with first_seen in last 10s
+SELECT COUNT(*) FROM events WHERE first_seen >= EXTRACT(EPOCH FROM NOW())::BIGINT - 10;
+-- Kind distribution
+SELECT kind, COUNT(*) FROM events GROUP BY kind ORDER BY count DESC;
+```
+
+**api/config.php** — Configuration (all config keys, not just caching):
+```sql
+SELECT key, value FROM config ORDER BY key;
+-- POST: UPDATE config SET value = ? WHERE key = ?
+```
+
+**api/subscriptions.php** — Subscription details:
+```sql
+SELECT * FROM subscriptions ORDER BY created_at DESC LIMIT 100;
+SELECT * FROM subscription_metrics ORDER BY id DESC LIMIT 100;
+```
+
+**api/auth.php** — Auth rules:
+```sql
+SELECT * FROM auth_rules ORDER BY id;
+```
+
+**api/ipbans.php** — IP bans:
+```sql
+SELECT * FROM ip_bans ORDER BY banned_at DESC;
+-- POST: INSERT/DELETE
+```
+
+**api/events.php** — Recent relay events (paginated):
+```sql
+SELECT id, pubkey, kind, created_at, content, tags
+FROM events ORDER BY created_at DESC LIMIT 50 OFFSET ?;
+-- Filter by kind, pubkey
+```
+
+**api/caching.php** — Caching status (reuse from admin/):
+```sql
+SELECT * FROM caching_service_state;
+SELECT * FROM caching_followed_pubkeys LIMIT 50 OFFSET ?;
+SELECT * FROM caching_backfill_relay_progress LIMIT 50 OFFSET ?;
+```
+
+**api/dm.php** — Direct messages:
+```sql
+SELECT id, pubkey, kind, created_at, content
+FROM events WHERE kind IN (4, 14, 15) ORDER BY created_at DESC LIMIT 50;
+```
+
+**api/query.php** — SQL query (admin only):
+```sql
+-- Execute arbitrary SELECT, return JSON
+-- Read-only by default; write mode behind a flag
+```
+
+### Step 5 — Event-rate chart
+- Canvas element in the statistics section
+- JS maintains `eventRateHistory = []` (max 60 points)
+- On each 10s refresh, push new event count, shift if > 60
+- Render as line chart on canvas (no external library)
+- X-axis labels: time (mm:ss), one tick per 10s
+- Y-axis labels: event count per interval
+
+### Step 6 — nginx config
+Add `/admin2/` location block (same pattern as `/admin/`):
+```nginx
+location ^~ /admin2/ {
+ alias /opt/c-relay-pg/admin2/;
+ index index.php;
+ auth_basic "Relay Admin";
+ auth_basic_user_file /opt/c-relay-pg/admin/.htpasswd;
+ location ~ \.php$ {
+ fastcgi_pass unix:/run/php/php8.3-fpm.sock;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $request_filename;
+ }
+ location ^~ /admin2/lib/ { deny all; }
+}
+```
+
+### Step 7 — Deploy and test
+- Copy `admin2/` to server
+- Add nginx location block, reload
+- Test all 9 sections
+- Verify 10s auto-refresh on statistics page
+- Verify event-rate chart updates
+
+## Key Differences from Original api/
+
+| Aspect | Original (api/) | New (admin2/) |
+|--------|-----------------|---------------|
+| Auth | NIP-07 Nostr extension login | HTTP Basic Auth (nginx) |
+| Data fetch | WebSocket + kind-23456 encrypted admin commands | PHP AJAX `fetch()` to `api/*.php` |
+| Encryption | NIP-44 (64KB limit) | None (direct DB, no limit) |
+| Real-time | WebSocket push | 10s polling |
+| Backend | C relay binary (embedded JS) | PHP-FPM + PostgreSQL PDO |
+| Sections | 9 (same) | 9 (same HTML, different data source) |
+
+## Out of Scope
+
+- NIP-07 Nostr login (use HTTP Basic Auth)
+- WebSocket push (use 10s polling)
+- Write operations that require relay-level logic (event publishing, NIP-42 auth) — those stay on the Nostr admin API
+- The original `api/` page remains unchanged and functional
diff --git a/plans/forward_catchup_plan.md b/plans/forward_catchup_plan.md
new file mode 100644
index 0000000..4ac6877
--- /dev/null
+++ b/plans/forward_catchup_plan.md
@@ -0,0 +1,306 @@
+# Forward Catch-Up Plan: Bridging the Gap When Caching Resumes
+
+## Problem
+
+When caching is turned off (or the caching service stops), events posted by
+followed authors during the downtime are missed. The current backfill walks
+**backward** from `until_cursor` toward the beginning of time — it does not
+cover events **newer** than the cursor. The live subscriber uses
+`since = time(NULL)`, so it only catches events from the moment it connects.
+Events in the gap between "last event we know about" and "caching resumed"
+are lost.
+
+## Schema Change: `last_event_at` column
+
+Add a `last_event_at BIGINT NOT NULL DEFAULT 0` column to
+`caching_followed_pubkeys`. This records the `created_at` of the most
+recent event we've ever seen for this author — not a proxy like
+`updated_at` (which records when we last *touched* the row, which could
+be a zero-event progress write or a follow-graph refresh).
+
+`since = last_event_at + 1, until = now` is exact: it catches every
+event the relay doesn't yet have, with no assumptions about when the
+last event occurred relative to when we last checked.
+
+### Population
+
+- **Backfill**: when publishing a page of events, compute
+ `max(created_at)` across the page (we already compute
+ `min(created_at)` for the cursor advance — add a parallel max).
+ Update `last_event_at = GREATEST(last_event_at, page_max_created_at)`.
+- **Live subscriber**: when an event is received and published, update
+ `last_event_at = GREATEST(last_event_at, event.created_at)`.
+- **One-time seed from `events` table**: on schema upgrade, set
+ `last_event_at = COALESCE((SELECT MAX(created_at) FROM events WHERE
+ pubkey = caching_followed_pubkeys.pubkey), 0)` for all existing rows.
+ This seeds the column from the relay's own data.
+
+### Migration
+
+Added to `src/pg_schema.sql` and `src/pg_schema.h`:
+
+```sql
+ALTER TABLE caching_followed_pubkeys
+ ADD COLUMN IF NOT EXISTS last_event_at BIGINT NOT NULL DEFAULT 0;
+
+-- One-time seed from the events table.
+UPDATE caching_followed_pubkeys fp
+ SET last_event_at = COALESCE(
+ (SELECT MAX(e.created_at) FROM events e WHERE e.pubkey = fp.pubkey),
+ 0
+ )
+ WHERE fp.last_event_at = 0;
+```
+
+No schema version bump needed — `ALTER TABLE ADD COLUMN IF NOT EXISTS`
+is idempotent and the seed `UPDATE` is guarded by `WHERE last_event_at = 0`.
+
+## Current Architecture
+
+### Tables
+
+**`caching_followed_pubkeys`** (per-author state):
+| Column | Purpose |
+|---|---|
+| `pubkey` | PK |
+| `until_cursor` | Unix timestamp; backfill queries `until = this`, walks backward |
+| `backfill_complete` | TRUE when drained to the beginning of time |
+| `events_fetched` | Cumulative count |
+| `last_seen` | Updated on follow-graph refresh |
+| `updated_at` | Updated on every backfill progress write and completion mark |
+| `last_event_at` | **NEW**: `created_at` of the most recent event we've seen for this author |
+
+**`caching_backfill_relay_progress`** (per-author-per-relay state):
+| Column | Purpose |
+|---|---|
+| `author_pubkey, relay_url` | Composite PK |
+| `until_cursor` | Per-relay backward-walk cursor |
+| `complete` | TRUE when this relay is drained for this author |
+| `updated_at` | Updated on every relay progress write |
+
+### Backfill flow ([`caching/src/backfill.c`](../caching/src/backfill.c:256))
+
+1. Pick next incomplete author (round-robin)
+2. For each incomplete relay for that author:
+ - Query `authors=[pk], until=until_cursor, limit=page_size`
+ - Publish events to the relay via the sink
+ - Advance `until_cursor` to `oldest_event_created_at - 1`
+ - If < page_size events + EOSE: mark relay `complete = TRUE`
+3. If all relays complete: mark author `backfill_complete = TRUE`
+
+### Restart behavior
+
+- **Normal restart** (no `--restart`): cursor and completion state preserved.
+ Backfill resumes the backward walk from stored cursors. **Gap not covered.**
+- **`--restart`**: all cursors reset to 0, all completion flags cleared. Full
+ re-drain from `now` backward. **Re-fetches everything** but covers the gap
+ incidentally (since it starts from `now`).
+
+### Live subscriber ([`caching/src/live_subscriber.c`](../caching/src/live_subscriber.c:86))
+
+Uses `since = time(NULL)` — only catches events from the moment it connects.
+No gap-bridging.
+
+## The Gap
+
+```
+Time ──────────────────────────────────────────────────────►
+ │ │ │
+ last_event_at caching now
+ (most recent event turned (caching
+ we know about) off resumed)
+ │
+ └── events posted here are missed
+```
+
+For **completed authors** (`backfill_complete = TRUE`): the backward walk is
+done. `last_event_at` tells us the most recent event we have. Events posted
+after `last_event_at` are in the gap.
+
+For **incomplete authors**: the backward walk is still in progress. The cursor
+is somewhere in the past, walking backward. Events newer than the cursor are
+not fetched by backfill. The live subscriber covers events from `now` forward.
+The gap is between `last_event_at` and `now`.
+
+## Solution: Forward Catch-Up Phase
+
+Add a **forward catch-up** phase that runs once when the caching service
+starts (or when backfill is re-enabled), before the normal backward-drain
+backfill loop begins.
+
+### Logic
+
+For **every** followed author (both complete and incomplete):
+1. Read `last_event_at` from `caching_followed_pubkeys`.
+2. If `last_event_at = 0`, skip (no events known yet — the backward drain
+ will handle it).
+3. Query one or two outbox relays:
+ `authors=[pk], since=last_event_at + 1, until=now, limit=page_size`
+4. Publish all returned events to the relay via the sink.
+5. Update `last_event_at` to the max `created_at` seen (or `now` if no
+ events were returned, to avoid re-querying the same empty window next
+ time).
+
+This is safe for both complete and incomplete authors:
+- **Complete authors**: the backward drain is done, so the forward catch-up
+ is the only thing needed.
+- **Incomplete authors**: the backward drain walks *below* `until_cursor`,
+ so events above `until_cursor` up to `last_event_at` were already fetched
+ during the initial drain (when `until_cursor` started at `now`). The
+ forward catch-up fills from `last_event_at + 1` to `now` — the gap that
+ formed while caching was off.
+
+### When to run
+
+- **On caching service startup** (not `--restart`, which does a full reset).
+- The caching service process starts when `caching_enabled` is turned on,
+ so this covers the "caching was off, now it's on" case.
+
+### Implementation
+
+#### 1. Schema: add `last_event_at` column
+
+In `src/pg_schema.sql` and `src/pg_schema.h`:
+
+```sql
+ALTER TABLE caching_followed_pubkeys
+ ADD COLUMN IF NOT EXISTS last_event_at BIGINT NOT NULL DEFAULT 0;
+
+UPDATE caching_followed_pubkeys fp
+ SET last_event_at = COALESCE(
+ (SELECT MAX(e.created_at) FROM events e WHERE e.pubkey = fp.pubkey),
+ 0
+ )
+ WHERE fp.last_event_at = 0;
+```
+
+#### 2. New function: `pg_inbox_get_authors_for_catchup()`
+
+In `caching/src/pg_inbox.c`:
+
+```c
+/* Returns a cJSON array of {pubkey, last_event_at} objects for all
+ * followed authors where last_event_at > 0. Caller must cJSON_Delete().
+ * Returns NULL on error. */
+cJSON* pg_inbox_get_authors_for_catchup(void);
+```
+
+SQL:
+```sql
+SELECT pubkey, last_event_at
+ FROM caching_followed_pubkeys
+ WHERE last_event_at > 0
+ ORDER BY last_event_at ASC
+```
+
+#### 3. New function: `pg_inbox_update_last_event_at()`
+
+```c
+/* Update last_event_at for a pubkey to the max of current and new value. */
+int pg_inbox_update_last_event_at(const char *pk, long event_created_at);
+```
+
+SQL:
+```sql
+UPDATE caching_followed_pubkeys
+ SET last_event_at = GREATEST(last_event_at, $2::BIGINT)
+ WHERE pubkey = $1
+```
+
+#### 4. New function: `cr_forward_catchup()`
+
+In a new file `caching/src/forward_catchup.c`:
+
+```c
+/* Run forward catch-up for all followed authors.
+ * For each author with last_event_at > 0, query events from
+ * last_event_at + 1 to now and publish them to the sink.
+ * Returns 0 on success, -1 on error. */
+int cr_forward_catchup(cr_config_t *cfg,
+ nostr_relay_pool_t *upstream,
+ cr_sink_t *sink);
+```
+
+Flow:
+1. Call `pg_inbox_get_authors_for_catchup()` to get the list.
+2. For each author:
+ a. Get the author's outbox relays from `caching_backfill_relay_progress`
+ (any relay, since we just need one good source).
+ b. Query `authors=[pk], since=last_event_at + 1, until=now,
+ limit=page_size` on one relay.
+ c. Publish all returned events to the sink.
+ d. If events were returned, update `last_event_at` to the max
+ `created_at` in the batch. If no events, update `last_event_at`
+ to `now` (so we don't re-query the same empty window).
+3. Log: "forward catch-up: N authors checked, M events published".
+
+#### 5. Update backfill to maintain `last_event_at`
+
+In `caching/src/backfill.c`, in the page-publishing loop (around line 375):
+- Add a `find_newest_created_at()` helper (parallel to the existing
+ `find_oldest_created_at()`).
+- After publishing a page, call
+ `pg_inbox_update_last_event_at(pk, newest_created_at)`.
+
+#### 6. Update live subscriber to maintain `last_event_at`
+
+In `caching/src/live_subscriber.c`, in the event-received callback:
+- Extract `created_at` from the event.
+- Call `pg_inbox_update_last_event_at(pubkey, created_at)`.
+
+#### 7. Call from `main.c`
+
+In `caching/src/main.c`, after relay discovery and followed-set sync,
+before the main loop:
+
+```c
+/* Forward catch-up: bridge the gap for all followed authors. */
+if (cfg->backfill.enabled && pg_conn && !restart) {
+ DEBUG_INFO("forward catch-up: checking for missed events");
+ cr_forward_catchup(&cfg, upstream, &sink);
+}
+```
+
+This runs once at startup. It's not throttled — it's a one-time pass.
+
+### Edge cases
+
+- **`--restart` flag**: full reset already starts from `now`, so forward
+ catch-up is skipped. The `last_event_at` seed from the `events` table
+ will set it to the most recent known event, and the backward drain from
+ `now` will cover everything.
+- **`last_event_at = 0`**: author has no known events. Skip — the backward
+ drain handles it.
+- **Very large gap** (caching off for months): the forward catch-up query
+ may return many events. Use `limit = page_size` and paginate if needed.
+ The relay's own dedup (unique index on event ID) handles duplicates.
+- **Relay doesn't support `since`**: most Nostr relays support `since`/
+ `until` (NIP-01). If ignored, the relay returns all events — dedup
+ handles it.
+
+### Files to change
+
+| File | Change |
+|---|---|
+| `src/pg_schema.sql` | `ALTER TABLE` add `last_event_at` + seed from `events` |
+| `src/pg_schema.h` | Mirror the above |
+| `caching/src/forward_catchup.c` | New: `cr_forward_catchup()` |
+| `caching/src/forward_catchup.h` | New: declaration |
+| `caching/src/pg_inbox.c` | New: `pg_inbox_get_authors_for_catchup()`, `pg_inbox_update_last_event_at()` |
+| `caching/src/pg_inbox.h` | New: declarations |
+| `caching/src/backfill.c` | Add `find_newest_created_at()`, call `pg_inbox_update_last_event_at()` after each page |
+| `caching/src/live_subscriber.c` | Call `pg_inbox_update_last_event_at()` on event receipt |
+| `caching/src/main.c` | Call `cr_forward_catchup()` at startup |
+| `caching/Makefile` | Add `forward_catchup.c` to sources |
+
+### Sequencing
+
+```mermaid
+graph TD
+ A[Caching service starts] --> B{Is --restart?}
+ B -- Yes --> C[Reset all progress, full re-drain from now]
+ B -- No --> D[Forward catch-up: all authors with last_event_at > 0]
+ D --> E[Normal backward-drain backfill loop]
+ E --> F[Live subscriber: since = now, ongoing]
+ F --> G[Live subscriber updates last_event_at on each event]
+ E --> H[Backfill updates last_event_at on each page]
diff --git a/plans/profile_cache_plan.md b/plans/profile_cache_plan.md
new file mode 100644
index 0000000..99a03a6
--- /dev/null
+++ b/plans/profile_cache_plan.md
@@ -0,0 +1,708 @@
+# Profile (kind-0) Cache Plan
+
+Make username/profile resolution a first-class c-relay-pg feature backed by a
+dedicated `profiles` table, replacing the three independent ad-hoc
+implementations that exist today. Phase 1 (name/metadata cache) is scoped for
+implementation now; Phase 2 (image caching) is designed here but deferred.
+
+## 0. Scope Boundary
+
+**In scope:** `src/` (schema + C data layer) and `admin/` (the PHP admin).
+
+**Out of scope:** the top-level [`api/`](../api/index.js) directory — the legacy
+embedded JS admin compiled into the binary via
+[`src/embedded_web_content.h`](../src/embedded_web_content.h) and served by
+[`handle_embedded_file_request()`](../src/api.c:1006). It is being replaced by
+`admin/` and is deliberately left untouched.
+
+Two consequences worth being explicit about:
+
+1. **Do not "fix" the duplicated logic in `api/index.js`.** It carries its own
+ copies of the profile-name preference ([`api/index.js:962`](../api/index.js:962))
+ and profile-picture handling ([`api/index.js:986`](../api/index.js:986)), plus
+ the same class of unescaped-`innerHTML` issue described in §2A.2. These are
+ knowingly left as-is because the whole tree is slated for removal. Note this
+ means the XSS exposure persists for as long as the embedded UI remains
+ reachable on the relay's HTTP port — a reason to prioritize retiring `api/`,
+ tracked separately from this plan.
+2. **The C-side changes still matter to both.** [`src/api.c`](../src/api.c) serves
+ the JSON that the embedded UI consumes, so the batching work in §2.5 benefits
+ `api/` incidentally. The C API must therefore stay backward-compatible: keep
+ emitting the existing `name` field (now the resolved value) alongside the new
+ `display_name` / `best_name` fields, so the legacy frontend keeps working
+ unchanged until it is deleted.
+
+---
+
+## 1. Current State
+
+Profile display-name resolution was introduced alongside the caching service and
+never generalized. There are **three separate implementations**, none cached:
+
+### 1.1 C backend — per-pubkey query inside a loop
+
+[`postgres_db_get_profile_metadata()`](../src/db_ops_postgres.c:2523) runs:
+
+```sql
+SELECT content FROM events WHERE pubkey = $1 AND kind = 0
+ORDER BY created_at DESC LIMIT 1
+```
+
+then `cJSON_Parse`es `content` and copies out eight known fields
+(`name`, `display_name`, `picture`, `about`, `nip05`, `website`, `lud16`, `lud06`).
+
+Dispatched through [`db_get_profile_metadata()`](../src/db_ops.c:189); the SQLite
+backend is a `NULL` stub ([`src/db_ops.c:389`](../src/db_ops.c:389)).
+
+Called from three places, **always inside a row loop** — a classic N+1:
+
+| Call site | Loop over | Queries per response |
+|---|---|---|
+| [`query_top_pubkeys()`](../src/api.c:503) | top 10 pubkeys | 10 |
+| [`src/api.c:1646`](../src/api.c:1646) | top pubkeys (2nd copy) | 10 |
+| caching follows status [`src/config.c:4298`](../src/config.c:4298) | every followed pubkey | 1 per follow (unbounded) |
+
+The `config.c` loop additionally issues a per-pubkey kind-count query
+([`src/config.c:4326`](../src/config.c:4326)), an outbox lookup
+([`src/config.c:4346`](../src/config.c:4346)) and a relay-progress query
+([`src/config.c:4356`](../src/config.c:4356)) — so a relay following 500 authors
+performs ~2000 queries to render one admin panel.
+
+The `display_name || name` preference logic is **duplicated verbatim** at all
+three call sites.
+
+### 1.2 PHP admin — repeated LATERAL joins
+
+[`admin/api/stats.php:90`](../admin/api/stats.php:90) and
+[`admin/api/caching.php:23`](../admin/api/caching.php:23) each hand-roll:
+
+```sql
+LEFT JOIN LATERAL (
+ SELECT content FROM events WHERE pubkey = e.pubkey AND kind = 0
+ ORDER BY created_at DESC LIMIT 1
+) p ON true
+... p.content::json->>'name', p.content::json->>'display_name'
+```
+
+with the same `$display_name ?: $name` fallback repeated in PHP. `content::json`
+re-parses the JSON text on every single admin page poll. The cast is also
+fragile: a malformed kind-0 `content` raises a PostgreSQL error that aborts the
+whole query (the `try/catch` then silently returns an empty result set).
+
+### 1.3 Browser JS — fetches from public relays
+
+[`loadUserProfile()`](../admin/assets/app.js:626) opens WebSocket connections to
+**third-party public relays** to fetch the logged-in admin's own kind-0, even
+though the relay's own database very likely has it. A third variant of the
+name-preference logic lives at [`admin/assets/app.js:650`](../admin/assets/app.js:650)
+(`profile.name || profile.display_name || profile.displayName`) — note this one
+prefers `name` over `display_name`, the **opposite** of the C and PHP versions,
+so the same user can render under two different names in one UI.
+
+Profile images are hotlinked straight to whatever URL the kind-0 contains
+([`admin/assets/app.js:653`](../admin/assets/app.js:653)), which leaks the admin's
+IP to arbitrary hosts and breaks silently on dead links.
+
+### 1.4 Conclusion
+
+A cache table is clearly warranted:
+
+- Kind 0 is **replaceable** — the unique index
+ [`uq_events_replaceable_pubkey_kind`](../src/pg_schema.sql:82) guarantees at
+ most one kind-0 row per pubkey. A `profiles` table is therefore a strict 1:1
+ projection of existing data and can be rebuilt from scratch at any time. No
+ risk of divergence-by-design.
+- Profiles change rarely but are read constantly.
+- Parsing JSON at write time (once per profile update) instead of read time
+ (every page poll × every row) is a large, cheap win.
+- One canonical name-preference rule fixes the inconsistency across the three
+ layers.
+
+---
+
+## 2. Phase 1 — `profiles` Table
+
+### 2.1 Schema
+
+Added to [`src/pg_schema.sql`](../src/pg_schema.sql) before the `COMMIT;` at
+[line 405](../src/pg_schema.sql:405), and mirrored into
+[`src/pg_schema.h`](../src/pg_schema.h) as escaped C string literals.
+
+```sql
+CREATE TABLE IF NOT EXISTS profiles (
+ pubkey TEXT PRIMARY KEY,
+ event_id TEXT NOT NULL,
+ created_at BIGINT NOT NULL,
+ name TEXT NOT NULL DEFAULT '',
+ display_name TEXT NOT NULL DEFAULT '',
+ about TEXT NOT NULL DEFAULT '',
+ picture TEXT NOT NULL DEFAULT '',
+ banner TEXT NOT NULL DEFAULT '',
+ nip05 TEXT NOT NULL DEFAULT '',
+ website TEXT NOT NULL DEFAULT '',
+ lud16 TEXT NOT NULL DEFAULT '',
+ lud06 TEXT NOT NULL DEFAULT '',
+ raw_content TEXT NOT NULL DEFAULT '',
+ parse_ok BOOLEAN NOT NULL DEFAULT TRUE,
+ updated_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT
+);
+
+CREATE INDEX IF NOT EXISTS idx_profiles_name ON profiles(name)
+ WHERE name <> '';
+CREATE INDEX IF NOT EXISTS idx_profiles_display_name ON profiles(display_name)
+ WHERE display_name <> '';
+CREATE INDEX IF NOT EXISTS idx_profiles_nip05 ON profiles(nip05)
+ WHERE nip05 <> '';
+```
+
+Notes:
+- **`name` and `display_name` are both stored verbatim, always.** Storing both
+ is free (they are short strings on a table with one row per pubkey), and it
+ means the question "which field do Nostr clients actually populate?" can be
+ answered from real data later rather than guessed at now — see
+ [§2.9](#29-which-field-do-people-actually-use). Neither field is ever
+ discarded, overwritten by the other, or collapsed into a single value at write
+ time.
+- There is deliberately **no generated `best_name` column.** An earlier draft
+ had one; it was wrong. A `STORED` generated column freezes the preference rule
+ into the schema, so changing which field is displayed would require a schema
+ migration and a full-table rewrite. Display preference is a presentation
+ decision and belongs at read time.
+- `raw_content` keeps the original JSON so non-standard fields (including
+ `displayName`, the camelCase variant some clients emit — see
+ [`admin/assets/app.js:650`](../admin/assets/app.js:650)) remain reachable
+ without re-querying `events`.
+- `parse_ok = FALSE` records "we saw a kind-0 but its content was not valid
+ JSON" — distinct from "no profile at all" (row absent). This makes the
+ malformed-JSON case explicit instead of an aborted query.
+- Empty-string defaults rather than `NULL` keep the C accessors branch-free.
+
+### 2.1.1 Display preference as configuration
+
+The preference rule lives in the existing `config` table
+([`src/pg_schema.sql:192`](../src/pg_schema.sql:192)) so it can be changed at
+runtime through the normal admin config path, with no migration:
+
+```sql
+INSERT INTO config (key, value, data_type, description, category, requires_restart)
+VALUES ('profile_name_preference', 'display_name',
+ 'string', 'Which kind-0 field to prefer for display: display_name or name',
+ 'display', 0)
+ON CONFLICT (key) DO NOTHING;
+```
+
+Valid values: `display_name` (prefer `display_name`, fall back to `name`) or
+`name` (the reverse). Default `display_name`, matching the current C and PHP
+behaviour so nothing visibly changes on upgrade.
+
+Each layer gets **one** resolver that reads this key — replacing the four
+scattered inline copies with one function per layer, while keeping the choice
+adjustable:
+
+```c
+// Applies profile_name_preference; falls back to the other field when the
+// preferred one is empty. Returns "" when neither is set (never NULL).
+const char* profile_display_name(const cJSON* profile);
+```
+
+Every profile object returned to a UI carries `name`, `display_name`, **and** the
+resolved `best_name`, so a consumer can render the resolved label while still
+having both raw values available.
+
+### 2.2 Population — PostgreSQL trigger
+
+A trigger keeps the table correct regardless of which process writes the event
+(relay ingest, the caching inbox poller, or a manual `psql` insert), so no
+writer can bypass it.
+
+```sql
+CREATE OR REPLACE FUNCTION sync_profile_from_event() RETURNS TRIGGER AS $$
+DECLARE
+ j JSONB;
+BEGIN
+ IF NEW.kind <> 0 THEN
+ RETURN NEW;
+ END IF;
+
+ BEGIN
+ j := NEW.content::jsonb;
+ IF jsonb_typeof(j) <> 'object' THEN
+ j := NULL;
+ END IF;
+ EXCEPTION WHEN others THEN
+ j := NULL;
+ END;
+
+ INSERT INTO profiles (
+ pubkey, event_id, created_at,
+ name, display_name, about, picture, banner,
+ nip05, website, lud16, lud06,
+ raw_content, parse_ok, updated_at
+ ) VALUES (
+ NEW.pubkey, NEW.id, NEW.created_at,
+ COALESCE(j->>'name',''),
+ COALESCE(j->>'display_name',''),
+ COALESCE(j->>'about',''),
+ COALESCE(j->>'picture',''),
+ COALESCE(j->>'banner',''),
+ COALESCE(j->>'nip05',''),
+ COALESCE(j->>'website',''),
+ COALESCE(j->>'lud16',''),
+ COALESCE(j->>'lud06',''),
+ NEW.content, (j IS NOT NULL),
+ EXTRACT(EPOCH FROM NOW())::BIGINT
+ )
+ ON CONFLICT (pubkey) DO UPDATE SET
+ event_id = EXCLUDED.event_id,
+ created_at = EXCLUDED.created_at,
+ name = EXCLUDED.name,
+ display_name = EXCLUDED.display_name,
+ about = EXCLUDED.about,
+ picture = EXCLUDED.picture,
+ banner = EXCLUDED.banner,
+ nip05 = EXCLUDED.nip05,
+ website = EXCLUDED.website,
+ lud16 = EXCLUDED.lud16,
+ lud06 = EXCLUDED.lud06,
+ raw_content = EXCLUDED.raw_content,
+ parse_ok = EXCLUDED.parse_ok,
+ updated_at = EXCLUDED.updated_at
+ -- Never let an older kind-0 overwrite a newer one.
+ WHERE EXCLUDED.created_at >= profiles.created_at;
+
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS trg_events_sync_profile ON events;
+CREATE TRIGGER trg_events_sync_profile
+AFTER INSERT OR UPDATE OF content ON events
+FOR EACH ROW EXECUTE FUNCTION sync_profile_from_event();
+```
+
+The `NEW.kind <> 0` early return means the cost for the 99.9% of events that are
+not profiles is one integer comparison — negligible next to the two triggers
+already firing on every insert
+([`trg_events_set_derived_fields`](../src/pg_schema.sql:160),
+[`trg_events_sync_event_tags`](../src/pg_schema.sql:166),
+[`trg_notify_event_stored`](../src/pg_schema.sql:283)).
+
+**Deletion:** add a companion `AFTER DELETE` trigger removing the `profiles` row
+when its backing kind-0 is deleted (NIP-09 via [`src/nip009.c`](../src/nip009.c)),
+guarded on `OLD.kind = 0 AND profiles.event_id = OLD.id` so a delete of a
+superseded event does not drop a current profile.
+
+### 2.3 One-time backfill
+
+Existing databases already hold kind-0 events. Following the established
+guarded-migration pattern used for `d_tag_value`
+([`src/pg_schema.sql:57`](../src/pg_schema.sql:57)), the backfill runs only on
+the version transition, not on every boot:
+
+```sql
+DO $$
+BEGIN
+ IF COALESCE((SELECT value FROM schema_info WHERE key = 'version'), '0') < '6' THEN
+ INSERT INTO profiles (pubkey, event_id, created_at, name, display_name,
+ about, picture, banner, nip05, website, lud16, lud06,
+ raw_content, parse_ok)
+ SELECT e.pubkey, e.id, e.created_at,
+ COALESCE(c.j->>'name',''), COALESCE(c.j->>'display_name',''),
+ COALESCE(c.j->>'about',''), COALESCE(c.j->>'picture',''),
+ COALESCE(c.j->>'banner',''), COALESCE(c.j->>'nip05',''),
+ COALESCE(c.j->>'website',''), COALESCE(c.j->>'lud16',''),
+ COALESCE(c.j->>'lud06',''),
+ e.content, (c.j IS NOT NULL)
+ FROM events e
+ LEFT JOIN LATERAL (
+ SELECT CASE WHEN e.content ~ '^\s*\{' THEN
+ (SELECT x FROM jsonb(e.content::jsonb) AS x)
+ END AS j
+ ) c ON true
+ WHERE e.kind = 0
+ ON CONFLICT (pubkey) DO NOTHING;
+ END IF;
+END
+$$;
+```
+
+The `::jsonb` cast can still raise on malformed content. Implementation should
+use a small `PL/pgSQL` loop with a per-row `EXCEPTION` block, or a
+`safe_jsonb(text)` helper function marked `IMMUTABLE` that returns `NULL` on
+parse failure — cleaner and reusable by the trigger too. Prefer the
+`safe_jsonb()` helper and use it in both the trigger and the backfill.
+
+Bump `EMBEDDED_PG_SCHEMA_VERSION` from `"5"` to `"6"` in
+[`src/pg_schema.h:4`](../src/pg_schema.h:4) and the `schema_info` insert at
+[`src/pg_schema.sql:260`](../src/pg_schema.sql:260).
+
+`postgres_db_apply_schema()` ([`src/db_ops_postgres.c:138`](../src/db_ops_postgres.c:138))
+runs the whole embedded script at startup, so existing deployments upgrade
+automatically. There is no generator script for `pg_schema.h` — it is a
+hand-maintained mirror, so **both files must be edited and kept identical**.
+
+### 2.4 C API
+
+Replace the single-row helper with a batch-capable pair in
+[`src/db_ops.h`](../src/db_ops.h:144):
+
+```c
+// Single profile from the profiles cache. NULL if no profile is cached.
+// Result object always contains "name", "display_name" (each possibly "")
+// and the resolved "best_name". Caller must cJSON_Delete().
+cJSON* db_get_profile(const char* pubkey);
+
+// Batch lookup: one query for many pubkeys. Returns an object keyed by
+// pubkey hex -> profile object. Pubkeys with no cached profile are absent.
+// Caller must cJSON_Delete().
+cJSON* db_get_profiles(const char** pubkeys, int count);
+```
+
+`db_get_profiles()` issues a single `WHERE pubkey = ANY($1::text[])` query,
+collapsing the N+1 loops into one round trip.
+
+`db_get_profile_metadata()` is retained as a deprecated thin wrapper over
+`db_get_profile()` so nothing breaks mid-refactor, then removed once all call
+sites are migrated.
+
+SQLite stubs in [`src/db_ops.c:389`](../src/db_ops.c:389) continue returning
+`NULL` — the `profiles` table is PostgreSQL-only, consistent with how the
+caching tables are handled.
+
+### 2.5 C call-site migration
+
+| File | Change |
+|---|---|
+| [`src/api.c:502`](../src/api.c:502) | Collect the 10 pubkeys, one `db_get_profiles()` call, then attach `name`/`display_name`/`best_name`/`picture` from the map. Replace the inline preference logic with `profile_display_name()`. |
+| [`src/api.c:1645`](../src/api.c:1645) | Same. Consider factoring the two near-identical blocks into one shared `api_attach_profile_fields()` helper. |
+| [`src/config.c:4297`](../src/config.c:4297) | Batch all followed pubkeys up front (they are already fully enumerated by the outer query) and look them up from the returned map inside the loop. |
+
+The `config.c` loop's other per-row queries (kind counts, relay progress) are
+out of scope here but are noted as the next optimization target — they can
+become two `GROUP BY` queries executed once.
+
+### 2.6 PHP migration
+
+Add one helper to [`admin/lib/helpers.php`](../admin/lib/helpers.php):
+
+```php
+/**
+ * Batch-resolve profiles from the cache.
+ * Returns [pubkey_hex => ['name'=>..., 'display_name'=>...,
+ * 'best_name'=>..., 'picture'=>..., 'nip05'=>...]].
+ */
+function profile_map(array $pubkeys): array
+
+/** Applies the profile_name_preference config key. Never returns null. */
+function profile_display_name(array $profile): string
+```
+
+`profile_map()` is a single parameterized `WHERE pubkey = ANY(...)` query against
+`profiles`, returning both raw name fields plus the resolved label. Then:
+
+- [`admin/api/stats.php:90`](../admin/api/stats.php:90) — drop the
+ `LEFT JOIN LATERAL` and the `content::json` casts; the top-pubkeys query
+ becomes a plain `GROUP BY e.pubkey`, and names come from `profile_map()`.
+ This also removes the `GROUP BY e.pubkey, p.content` grouping-by-a-JSON-blob
+ wart.
+- [`admin/api/caching.php:23`](../admin/api/caching.php:23) — same; or simply
+ `LEFT JOIN profiles p ON p.pubkey = fp.pubkey` and select `p.name,
+ p.display_name`, which is a cheap indexed join now that no subquery or parsing
+ is involved.
+- Replace both copies of the `$display_name ?: $name` fallback with
+ `profile_display_name()`.
+
+### 2.7 JS migration
+
+- Add a read-only admin endpoint (`admin/api/profile.php?pubkey=...`) returning
+ the cached profile.
+- [`loadUserProfile()`](../admin/assets/app.js:626) tries that endpoint first and
+ only falls back to public relays if the relay has no cached kind-0 for the
+ logged-in admin (a real possibility for a fresh relay), then keeps the existing
+ render path.
+- Consume the server-provided `best_name` instead of re-deriving a preference in
+ the browser, so all three layers finally agree and the JS copy at
+ [`admin/assets/app.js:650`](../admin/assets/app.js:650) — which currently
+ prefers `name`, the opposite of C and PHP — stops disagreeing. Keep
+ `displayName` (camelCase) handling only in the public-relay fallback path,
+ where raw client JSON is parsed directly.
+
+---
+
+## 2A. Hostile Characters in Names
+
+Nostr names are attacker-controlled free-form UTF-8. The guiding principle:
+
+> **Store bytes verbatim. Neutralize at the point of rendering.**
+
+Sanitizing at write time would be wrong — it is lossy, irreversible, and the
+"correct" transformation differs per output context (HTML body vs. attribute vs.
+JSON vs. CSV vs. terminal log). A name mangled on the way into the cache can
+never be recovered, and the cache would no longer faithfully mirror the kind-0
+event. So the cache table stores exactly what the user published.
+
+**But "it's a frontend issue" is only ~90% true.** There is one true storage-layer
+concern, and one place where the current frontend is actively unsafe.
+
+### 2A.1 Storage-layer concern: NUL bytes (must handle at write time)
+
+PostgreSQL `TEXT` **cannot** store `U+0000`. A kind-0 containing `\u0000` in its
+JSON string makes `->>` yield a value that PostgreSQL refuses to store, raising
+`ERROR: unsupported Unicode escape sequence` — which would abort the trigger and
+therefore **reject the whole event insert**. That turns a cosmetic nuisance into
+a denial-of-service on event ingestion.
+
+This is not a presentation problem and must be handled in the trigger:
+
+```sql
+-- Strip NUL only; everything else is preserved byte-for-byte.
+replace(COALESCE(j->>'name',''), E'\\u0000', '')
+```
+
+Implement as a small `sanitize_pg_text(text)` helper used for every extracted
+string column. It removes **only** characters PostgreSQL structurally cannot
+store — not "weird" characters generally. Invalid UTF-8 byte sequences are
+already rejected earlier by `cJSON` parsing and by the `safe_jsonb()` helper
+(the row lands with `parse_ok = FALSE`), so no additional handling is needed.
+
+A defensive `byte_size` guard is also worth adding: cap stored name fields at a
+sane length (e.g. 1 KB) so a megabyte-long "name" cannot bloat the table or the
+admin JSON payloads. Truncation is recorded in `raw_content`, which keeps the
+full original.
+
+### 2A.2 Live vulnerability: stored XSS in the admin UI
+
+This must be fixed as part of this work, because the whole point of the change is
+to route more user-controlled names into more admin pages.
+
+[`admin/assets/app.js:142`](../admin/assets/app.js:142) interpolates the name
+directly into `innerHTML`:
+
+```js
+tbody.innerHTML = d.top_pubkeys.map((p, i) =>
+ `${i+1} ${p.name || 'unknown '} ...`
+```
+
+and [`admin/assets/app.js:411`](../admin/assets/app.js:411) does the same for the
+caching-follows table. A user who sets their kind-0 `name` to
+` ` achieves **script execution in the relay
+administrator's authenticated browser session** merely by posting enough events
+to appear in the top-pubkeys list. No privileged access is required.
+
+The codebase is already inconsistent about this: the header name at
+[`app.js:652`](../admin/assets/app.js:652) correctly uses `textContent` and is
+safe. The table renderers are not.
+
+**Fix:** add an escaping helper and apply it to every interpolated
+user-controlled value in `innerHTML` template strings:
+
+```js
+const esc = (s) => String(s ?? '').replace(/[&<>"']/g,
+ c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
+```
+
+Auditing the surrounding rows shows the same pattern applied to other
+user-controlled fields — event `content` ([`app.js:380`](../admin/assets/app.js:380)),
+DM content ([`app.js:441`](../admin/assets/app.js:441)), config values
+([`app.js:219`](../admin/assets/app.js:219)), and auth-rule `pattern_value`
+([`app.js:251`](../admin/assets/app.js:251)) — so the sweep should cover all of
+them, not just names. Preferring `textContent` / `createElement` over `innerHTML`
+in these renderers is the more durable fix where it is not too invasive.
+
+Note the PHP side is already correct: [`e()`](../admin/lib/helpers.php:9) wraps
+`htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` and is used for server-rendered
+output. The gap is purely in the JS-built tables.
+
+### 2A.3 Presentation-layer nuisances (frontend, cosmetic)
+
+These stay unsanitized in the database and are handled with CSS/formatting:
+
+| Issue | Effect | Mitigation |
+|---|---|---|
+| Bidi overrides (`U+202E` RTL) | Reverses surrounding text, spoofs other names | Render names in a `` element — purpose-built for exactly this, isolates bidi without altering the value |
+| Zalgo / stacked combining marks | Vertical overflow past row bounds | `overflow: hidden` + fixed line-height on the name cell |
+| Zero-width chars (`U+200B`, `U+FEFF`) | Invisible; two names look identical | Optional: reveal-on-hover indicator; do not strip |
+| Newlines / tabs | Break single-line table layout | CSS `white-space: nowrap` + `text-overflow: ellipsis` |
+| Very long names | Blow out column width | CSS `max-width` + ellipsis (value stays intact in a `title` tooltip) |
+| Emoji / astral-plane chars | None — legitimate usage | Nothing; ensure JS length math uses code points, not UTF-16 units, when truncating |
+
+Truncation in JS deserves care: `substring()` on a UTF-16 string can split a
+surrogate pair and emit a replacement glyph. Use `Array.from(str).slice(0, n)` or
+CSS-based ellipsis (preferred — no string surgery at all).
+
+### 2A.4 Terminal/log safety
+
+Names flow into `DEBUG_*` output. ANSI escape sequences in a name can manipulate
+a maintainer's terminal. Log rendering should escape non-printable bytes, or
+simply avoid logging profile names at all — the pubkey is the useful identifier
+in logs anyway.
+
+### 2.9 Which field do people actually use?
+
+Storing both fields turns this into an empirical question rather than a guess.
+Once the table is populated, one aggregate query answers it against real data
+from your relay's own corpus:
+
+```sql
+SELECT count(*) FILTER (WHERE name <> '' AND display_name <> '') AS both,
+ count(*) FILTER (WHERE name <> '' AND display_name = '') AS name_only,
+ count(*) FILTER (WHERE name = '' AND display_name <> '') AS display_only,
+ count(*) FILTER (WHERE name = '' AND display_name = '') AS neither,
+ count(*) FILTER (WHERE name <> '' AND display_name <> ''
+ AND name <> display_name) AS both_differ,
+ count(*) AS total
+ FROM profiles;
+```
+
+`both_differ` is the number that matters: it counts profiles where the preference
+setting actually changes what gets rendered. If it is near zero, the setting is
+academic and either default is fine. If it is large, the setting earns its keep.
+
+Worth surfacing as a small panel on the admin stats page — it is one cheap
+aggregate over a table with one row per pubkey, and it makes
+`profile_name_preference` self-documenting: you can see the impact of the choice
+before making it. Add it once the table has accumulated real data.
+
+### 2.8 Verification
+
+- Fresh database: relay starts, `profiles` exists, posting a kind-0 populates
+ exactly one row with `name` and `display_name` both preserved verbatim.
+- Upgrade path: start against a database with pre-existing kind-0 events, confirm
+ the backfill fills every row once and does **not** re-run on the next restart.
+- Replaceable-update: publish a newer kind-0, confirm the row updates; replay an
+ older one, confirm the row does **not** regress.
+- Malformed content: store a kind-0 whose content is not JSON; confirm the insert
+ still succeeds, `parse_ok = FALSE`, and the admin pages render without error.
+- **NUL byte:** publish a kind-0 whose `name` contains `\u0000`; confirm the
+ event is still accepted, the profile row is created, and the relay does not
+ error. This is the regression test for the ingest-DoS path in §2A.1.
+- **XSS:** publish a kind-0 with `name` set to
+ ` `, load the stats and caching pages, and
+ confirm the markup is rendered as visible text and `window.__xss` is
+ undefined.
+- **Bidi/Zalgo:** publish names containing `U+202E` and stacked combining marks;
+ confirm table layout and neighbouring rows are unaffected.
+- Consistency: the same pubkey shows an identical name in the stats table, the
+ caching follows table, and the header.
+- Query-count check: confirm the top-pubkeys API response issues one profile
+ query rather than ten.
+- Both-fields check: query `profiles` for a pubkey whose kind-0 sets `name` and
+ `display_name` to different values; confirm both are stored distinctly.
+
+---
+
+## 3. Phase 2 — Image Caching (design only, deferred)
+
+Recorded here so Phase 1's schema does not need reworking later.
+
+### 3.1 Motivation
+
+Today the admin UI hotlinks `picture` URLs directly
+([`admin/assets/app.js:653`](../admin/assets/app.js:653)). Problems: the admin's
+browser reveals its IP to arbitrary third-party hosts on every page load; dead
+or slow hosts degrade the UI; images can be arbitrarily large; there is no way to
+show avatars offline.
+
+### 3.2 Proposed schema
+
+```sql
+CREATE TABLE IF NOT EXISTS profile_images (
+ pubkey TEXT PRIMARY KEY,
+ source_url TEXT NOT NULL,
+ mime_type TEXT NOT NULL DEFAULT '',
+ byte_size INTEGER NOT NULL DEFAULT 0,
+ sha256 TEXT NOT NULL DEFAULT '',
+ etag TEXT NOT NULL DEFAULT '',
+ image_data BYTEA,
+ fetch_state TEXT NOT NULL DEFAULT 'pending',
+ fetch_attempts INTEGER NOT NULL DEFAULT 0,
+ last_error TEXT,
+ fetched_at BIGINT NOT NULL DEFAULT 0,
+ updated_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT,
+ CHECK (fetch_state IN ('pending','ok','failed','skipped','too_large'))
+);
+CREATE INDEX IF NOT EXISTS idx_profile_images_pending
+ ON profile_images(fetch_state, fetch_attempts) WHERE fetch_state = 'pending';
+```
+
+`BYTEA` in PostgreSQL rather than the filesystem keeps backup/restore and the
+container story single-artifact, matching how everything else in this project is
+stored. Avatars are small; a cap keeps total size bounded.
+
+### 3.3 Fetch worker
+
+`libcurl` is already linked ([`Makefile:6`](../Makefile:6)) but currently unused
+in `src/`. A worker modeled on
+[`caching_inbox_poller.c`](../src/caching_inbox_poller.c) — two-state
+idle/active polling, config-gated, off the main libwebsockets thread — would:
+
+1. Enqueue `pending` rows when `profiles.picture` changes (trigger or poll).
+2. Fetch with a hard timeout, a max-bytes ceiling (~256 KB), redirect limit,
+ and `Content-Type` allow-list (`image/png|jpeg|webp|gif`).
+3. Send `If-None-Match` on refresh, honour `304`.
+4. Exponential backoff, capped `fetch_attempts`, terminal `failed`.
+
+New config keys following existing naming: `profile_image_cache_enabled`
+(default **off**), `profile_image_max_bytes`, `profile_image_refresh_days`,
+`profile_image_fetch_concurrency`.
+
+### 3.4 Serving
+
+A relay HTTP route `/avatar/` handled in
+[`handle_embedded_file_request()`](../src/api.c:1006) (called from
+[`src/websockets.c:1261`](../src/websockets.c:1261)), returning the bytes with a
+long `Cache-Control` and an `ETag`, falling back to a generated identicon or
+`404` when uncached. The UI then only ever loads images from the relay's own
+origin.
+
+### 3.5 Risks to weigh before committing
+
+- **Outbound HTTP from the relay** is a new capability and a real SSRF surface —
+ needs a private-IP/localhost block-list and scheme restriction. This is the
+ main reason to keep it default-off and deferred.
+- Database growth: bounded by `max_bytes × profile count`; needs a documented
+ ceiling and a prune path.
+- Content risk: the relay would be re-serving arbitrary third-party bytes under
+ its own origin. Strict `Content-Type` enforcement plus
+ `Content-Security-Policy` / `X-Content-Type-Options: nosniff` on the route.
+
+---
+
+## 4. Files Touched (Phase 1)
+
+| File | Change |
+|---|---|
+| [`src/pg_schema.sql`](../src/pg_schema.sql) | `profiles` table (both name fields, no generated column), `safe_jsonb()`, `sanitize_pg_text()`, sync + delete triggers, guarded backfill, `profile_name_preference` config default, version → 6 |
+| [`src/pg_schema.h`](../src/pg_schema.h) | Mirror the above as C string literals; bump `EMBEDDED_PG_SCHEMA_VERSION` |
+| [`src/db_ops.h`](../src/db_ops.h) | Declare `db_get_profile()` / `db_get_profiles()` |
+| [`src/db_ops_postgres.h`](../src/db_ops_postgres.h) | Declare the postgres implementations |
+| [`src/db_ops_postgres.c`](../src/db_ops_postgres.c) | Implement both against `profiles`; retire the events-table query |
+| [`src/db_ops.c`](../src/db_ops.c) | Dispatch entries + SQLite stubs |
+| [`src/config.h`](../src/config.h) / [`src/config.c`](../src/config.c) | `profile_display_name()` resolver; batch profile lookup in the caching follows loop |
+| [`src/api.c`](../src/api.c) | Batch both top-pubkeys loops; shared attach helper emitting `name` + `display_name` + `best_name` |
+| [`admin/lib/helpers.php`](../admin/lib/helpers.php) | `profile_map()` + `profile_display_name()` |
+| [`admin/api/stats.php`](../admin/api/stats.php) | Use `profile_map()`; drop LATERAL + JSON casts |
+| [`admin/api/caching.php`](../admin/api/caching.php) | Join `profiles`; drop LATERAL + JSON casts |
+| `admin/api/profile.php` | New: single-profile lookup endpoint |
+| [`admin/assets/app.js`](../admin/assets/app.js) | **`esc()` helper + XSS sweep of all `innerHTML` renderers (§2A.2)**; local-first profile load; consume server `best_name` |
+| [`admin/assets/index.css`](../admin/assets/index.css) | `nowrap` / `overflow` / `max-width` + ellipsis on name cells (§2A.3) |
+| `tests/` | New script covering populate / upgrade / replace / malformed / NUL / XSS / bidi cases |
+
+---
+
+## 5. Sequencing
+
+```mermaid
+graph TD
+ A[Add profiles table + safe_jsonb + sanitize_pg_text + triggers to pg_schema.sql] --> B[Mirror into pg_schema.h and bump version to 6]
+ B --> C[Guarded one-time backfill + profile_name_preference config default]
+ C --> D[Implement db_get_profile and db_get_profiles]
+ D --> E[Migrate api.c and config.c to batch lookups]
+ E --> F[Add profile_map helper and migrate PHP endpoints]
+ F --> X[Fix stored XSS: esc helper and innerHTML sweep in app.js]
+ X --> G[Add profile.php endpoint and update app.js profile load]
+ G --> Y[CSS hardening for hostile name rendering]
+ Y --> Z[Add name-field usage panel to stats page]
+ G --> H[Tests: populate, upgrade, replace, malformed, consistency]
+ H --> I[Phase 2 image caching - deferred]
+```
diff --git a/plans/server_side_ascii_chart_plan.md b/plans/server_side_ascii_chart_plan.md
new file mode 100644
index 0000000..a621270
--- /dev/null
+++ b/plans/server_side_ascii_chart_plan.md
@@ -0,0 +1,233 @@
+# Server-Side ASCII Chart Plan
+
+## Goal
+
+Replace the client-side `text_graph.js` ASCII chart with a **server-side PHP renderer** that produces the ASCII X-bar chart string. The chart is served from a **dedicated plain-text endpoint** (`api/chart.php`) that works both in the browser (injected into a ``) and in the terminal via `curl` — the ASCII art renders correctly either way.
+
+Four time ranges, each with its own bin size and refresh/caching strategy:
+
+| Range | Span | Bin size | Bins | Refresh | Cache TTL |
+|--------|-------------|------------|------|------------------|------------|
+| Hour | last 1h | 10 seconds | 360 | every 10s (live) | none |
+| Day | last 24h | 5 minutes | 288 | every 10s* | 1 hour |
+| Month | last 30d | 1 hour | 720 | every 10s* | 1 day |
+| Year | last 365d | 1 day | 365 | every 10s* | 1 month |
+
+\* The client polls every 10s, but the server only re-runs the expensive query when the cache expires. Between cache expirations, the cached ASCII string is returned instantly.
+
+## Architecture
+
+```mermaid
+flowchart TD
+ subgraph Clients
+ T[Terminal — curl]
+ W[Web UI — app.js]
+ end
+
+ T -->|GET chart.php?range=hour| EP[chart.php]
+ W -->|GET chart.php?range=hour| EP
+ T -->|GET chart.php?range=day| EP
+ W -->|GET chart.php?range=day| EP
+
+ EP --> C{Cache valid?}
+ C -->|Yes| Return[Return cached ASCII string]
+ C -->|No| Query[Run GROUP BY binning query]
+ Query --> Render[render_ascii_chart in lib/ascii_chart.php]
+ Render --> CacheWrite[Write to cache file]
+ CacheWrite --> Return
+
+ Return -->|text/plain; charset=utf-8| T
+ Return -->|text/plain; charset=utf-8| W
+
+ subgraph "Cache files (admin2/cache/)"
+ H[chart_hour.txt — never cached]
+ D[chart_day.txt — TTL 1h]
+ M[chart_month.txt — TTL 1d]
+ Y[chart_year.txt — TTL 1mo]
+ end
+```
+
+## Components
+
+### 1. PHP ASCII Chart Renderer — `admin2/lib/ascii_chart.php`
+
+A pure function that takes an array of bin counts and produces the ASCII X-bar chart string. Mirrors the layout of the original `text_graph.js`:
+
+```
+ New Events
+
+ 11 | X
+ 10 | X X
+ 9 | X X X X
+ 8 | X X X X X X X
+ 7 | X X X X X X X X X X
+ 6 | X X X X X X X X X X X X X
+ 5 | X X X X X X X X X X X X X X X
+ 4 | X X X X X X X X X X X X X X X X X
+ 3 |X X X X X X X X X X X X X X X X X X X
+ 2 |X X X X X X X X X X X X X X X X X X X
+ 1 |X X X X X X X X X X X X X X X X X X X
+ +----------------------------------------
+ 0s 50s 100s 150s 200s 250s 300s
+```
+
+**Function signature:**
+```php
+function render_ascii_chart(array $bins, array $options = []): string
+```
+
+**Options:**
+- `title` (string, default `'New Events'`)
+- `max_height` (int, default `11`) — chart height in rows
+- `x_axis_label` (string, default `''`)
+- `bin_duration` (int, seconds) — for X-axis elapsed-time labels
+- `label_interval` (int, default `5`) — label every N bins
+
+**Algorithm** (same as `text_graph.js` render method):
+1. `max_count = max($bins)`; `scale_factor = max(1, ceil(max_count / max_height))`
+2. For each row from `max_height` down to `1`:
+ - Y-axis label = `(row - 1) * scale_factor + 1`, right-padded to 3 chars
+ - For each bin: if `ceil(count / scale_factor) >= row` → `X`, else space
+3. X-axis: `+` followed by dashes (one per bin)
+4. X-axis labels: elapsed time every `label_interval` bins, formatted as `Ns` / `Nm` / `Hh` / `Dd` depending on magnitude
+
+### 2. Binning SQL Queries — in `stats.php`
+
+Each range uses a `FLOOR((created_at - epoch) / bin_size)` GROUP BY query. The `idx_events_created_at` index makes these fast.
+
+**Hour (live, no cache):**
+```sql
+SELECT FLOOR((created_at - :epoch) / 10)::INT AS bin, COUNT(*) AS cnt
+FROM events
+WHERE created_at >= :epoch
+GROUP BY bin
+ORDER BY bin;
+```
+- `epoch = now - 3600`, `bin_size = 10s`, produces up to 360 bins
+- Runs on every 10s poll (cheap: only scans last hour, indexed)
+
+**Day (cache TTL 1h):**
+```sql
+SELECT FLOOR((created_at - :epoch) / 300)::INT AS bin, COUNT(*) AS cnt
+FROM events
+WHERE created_at >= :epoch
+GROUP BY bin
+ORDER BY bin;
+```
+- `epoch = now - 86400`, `bin_size = 300s` (5 min), produces up to 288 bins
+
+**Month (cache TTL 1d):**
+```sql
+SELECT FLOOR((created_at - :epoch) / 3600)::INT AS bin, COUNT(*) AS cnt
+FROM events
+WHERE created_at >= :epoch
+GROUP BY bin
+ORDER BY bin;
+```
+- `epoch = now - 2592000`, `bin_size = 3600s` (1h), produces up to 720 bins
+
+**Year (cache TTL 1 month):**
+```sql
+SELECT FLOOR((created_at - :epoch) / 86400)::INT AS bin, COUNT(*) AS cnt
+FROM events
+WHERE created_at >= :epoch
+GROUP BY bin
+ORDER BY bin;
+```
+- `epoch = now - 31536000`, `bin_size = 86400s` (1 day), produces up to 365 bins
+
+**Bin array assembly:** Query returns only non-empty bins. PHP fills a fixed-length array (all zeros) and overlays the counts at the correct positions, so empty time slots show as blank columns — the chart always advances in time.
+
+### 3. File-Based Cache — `admin2/cache/`
+
+Simple file cache with TTL. No APCu/Redis dependency.
+
+```php
+function get_cached_chart(string $range): ?string
+function set_cached_chart(string $range, string $ascii): void
+```
+
+- Cache files: `admin2/cache/chart_{range}.txt`
+- TTLs: `hour` = 0 (never cache), `day` = 3600, `month` = 86400, `year` = 2592000
+- Check: `filemtime($file) > time() - $ttl`
+- Directory `admin2/cache/` created automatically with `mkdir(..., 0775, true)`
+
+### 4. Standalone Chart Endpoint — `admin2/api/chart.php`
+
+A dedicated plain-text endpoint that returns the raw ASCII chart string. Works in both the browser and the terminal.
+
+**Request:** `GET api/chart.php?range=hour|day|month|year`
+
+**Response:** `Content-Type: text/plain; charset=utf-8` — just the ASCII chart string, no JSON wrapper.
+
+**Terminal usage:**
+```bash
+curl http://localhost:8088/api/chart.php?range=hour
+curl http://localhost:8088/api/chart.php?range=day
+curl http://localhost:8088/api/chart.php?range=month
+curl http://localhost:8088/api/chart.php?range=year
+```
+
+**Logic:**
+1. Read `range` query param (default: `hour`)
+2. Validate against allowed ranges (`hour`, `day`, `month`, `year`)
+3. Check cache: if valid, return cached string immediately
+4. If cache miss/expired: run the binning SQL query, fill the bin array, call `render_ascii_chart()`, write to cache, return the string
+5. Set `Content-Type: text/plain; charset=utf-8` header
+
+**`stats.php` is unchanged** — it continues to return the existing JSON stats (numbers only). The chart is a completely separate endpoint, keeping concerns cleanly separated.
+
+### 5. Frontend Changes
+
+#### `admin2/index.php`
+- Replace the single chart div with a chart container + range selector tabs:
+ ```html
+
+ 1H
+ 1D
+ 1M
+ 1Y
+
+
Loading chart...
+ ```
+- Remove `