Admin Dashboard

The L2 node binary ships with a built-in admin dashboard — bundled HTML + JS served at /admin/dashboard. It shows node uptime, peer count, message stats, IPFS health, Klever sync height, anchor history, and gives operator controls for pause/resume and metadata publication. This step covers enabling it, exposing it securely through a reverse proxy, and logging in with a Klever wallet.

Enable the dashboard in the node config

Add an [api.admin] block to /etc/ogmara/ogmara.toml. The dashboard is off by default; you must opt in and list at least one wallet address authorized to log in remotely.

# In /etc/ogmara/ogmara.toml

[api.admin]
# Enable the admin endpoints (auth + dashboard + protected routes).
enabled = true

# Serve the bundled dashboard HTML at /admin/dashboard.
dashboard = true

# Wallets allowed to authenticate via the wallet-sign login flow.
# Multiple wallets supported — useful for team operators.
# Use bech32 (klv1...) addresses.
admin_wallets = [
  "klv1yourwalletaddresshere...",
]

# How long a successful login stays valid before re-auth is required.
session_ttl_hours = 24

Restart the node so the new admin routes are registered:

docker restart ogmara-l2
# or for source builds:
sudo systemctl restart ogmara-node

From localhost the dashboard requires no login (auth bypass for loopback connections) — useful for SSH-tunnel access during initial setup: ssh -L 41721:127.0.0.1:41721 user@your-server, then visit http://127.0.0.1:41721/admin/dashboard in your browser. Remote access (next subsection) requires wallet login.

Behind a non-loopback proxy, set [api] trusted_proxies. The localhost auth bypass and per-IP rate limits depend on the node resolving the real client IP. It only honours the proxy's X-Forwarded-For header when the connecting IP is trusted. A same-host Apache/Nginx connects over loopback (127.0.0.1), which is always implicitly trusted — no change needed. But if the node runs in a container with the proxy on the host, the node sees the Docker bridge IP rather than loopback, ignores the forwarded header, and treats every browser as the bridge IP — so it never recognizes a genuine localhost operator and rate-limits bucket everyone together. In that case add the bridge CIDR (e.g. "172.17.0.0/16") to [api] trusted_proxies. See Configuration.

Recommended: separate subdomain for the admin dashboard

Don't expose /admin/ on the same subdomain as the public API. The cleanest pattern is two subdomains pointing at the same node:

  • node.yourdomain.com — serves the public REST API + WebSocket (see Reverse Proxy for that vhost). Denies /admin/ so the admin endpoints can't be probed from the public-facing host.
  • stats.yourdomain.com (or admin.yourdomain.com) — serves the admin dashboard. Denies /api/ so accidental cross-pollination is impossible.

Point both subdomains at your server's public IP in DNS (A/AAAA records), then obtain a certificate for the new subdomain:

# Get a Let's Encrypt cert for the admin subdomain
# (re-uses the Apache plugin pattern from the Reverse Proxy page).
sudo certbot --apache -d stats.yourdomain.com

Apache vhost for the admin subdomain

# /etc/apache2/sites-available/ogmara-admin.conf

<VirtualHost *:80>
    ServerName stats.yourdomain.com
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerName stats.yourdomain.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/stats.yourdomain.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/stats.yourdomain.com/privkey.pem

    # Pass the real client IP so the L2 node can distinguish remote
    # browsers from local proxy connections.
    RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"

    # Proxy everything to the L2 node API port.
    ProxyPreserveHost On

    # WebSocket upgrade for /admin/dashboard/ws (live metric stream) — MUST come
    # before the catch-all ProxyPass and be excluded from it, or ProxyPass forwards
    # it as plain HTTP, the upgrade fails, and the dashboard's live metrics break.
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/admin/dashboard/ws(.*)$ ws://127.0.0.1:41721/admin/dashboard/ws$1 [P,L]

    ProxyPass /admin/dashboard/ws !
    ProxyPass / http://127.0.0.1:41721/
    ProxyPassReverse / http://127.0.0.1:41721/

    # Block public API endpoints on this subdomain — admin only.
    <LocationMatch "^/api/">
        Require all denied
    </LocationMatch>

    # Security headers
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
</VirtualHost>
sudo a2ensite ogmara-admin
sudo apache2ctl configtest
sudo systemctl reload apache2

Nginx alternative

# /etc/nginx/sites-available/ogmara-admin

server {
    listen 80;
    server_name stats.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name stats.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/stats.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stats.yourdomain.com/privkey.pem;

    add_header X-Frame-Options SAMEORIGIN always;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;

    # Block public API on this admin subdomain.
    location /api/ {
        return 403;
    }

    location / {
        proxy_pass http://127.0.0.1:41721;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support for /admin/dashboard/ws
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}
sudo ln -s /etc/nginx/sites-available/ogmara-admin /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

The dashboard URL

The dashboard is served at /admin/dashboardnot at the subdomain root. Use the full path in your browser:

https://stats.yourdomain.com/admin/dashboard

Common mistake: visiting https://stats.yourdomain.com/ (root) or https://stats.yourdomain.com/admin/ (trailing slash, no /dashboard) will return either a 404 or a bare authentication required text response — not the dashboard. Always use the full /admin/dashboard path.

Log in with your Klever wallet

Open https://stats.yourdomain.com/admin/dashboard in a browser that has the Klever Extension installed. You should see the dashboard's Connect Wallet login overlay.

  1. Click Connect Wallet.
  2. Approve the wallet connection in the Klever Extension popup.
  3. The dashboard requests a signature on a one-time challenge string. Sign it in the extension.
  4. On success the node issues a session cookie (HttpOnly, Secure, SameSite=Lax, expires after session_ttl_hours). The dashboard loads with live metrics streaming over WebSocket.

Only wallet addresses listed in [api.admin] admin_wallets can complete the login. Any other wallet gets a signature-verification rejection. Add multiple wallets if your operator team has several admins; each entry is independent and one wallet's session doesn't affect another.

What the dashboard shows

  • Overview — uptime, peers, message count, user count, channel count, live CPU + memory + disk sparklines, IPFS status, Klever sync height (and lag from chain tip).
  • Network — connected peer list with latency + direction + discovery source (book/config/SC/runtime), gossip mesh stats per topic.
  • Storage — RocksDB column-family sizes, IPFS pin count, snapshot cache status.
  • Messages — rejection counters by reason (signature, pow, rate-limit, etc.), useful for spotting attacks or misconfigured clients.
  • Alerts — rolling history of critical/warning/info alerts dispatched to Telegram/Discord/webhook.
  • Anchoring — current registration state, on-chain metadata, pause status, setNodeMetadata calldata builder for publishing your multiaddrs.
← Reverse Proxy   Next: Verify →