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.
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.
Don't expose /admin/ on the same subdomain as the public API. The cleanest pattern is two subdomains pointing at the same node:
/admin/ so the admin endpoints can't be probed from the public-facing host.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
# /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
# /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 is served at /admin/dashboard — not 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.
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.
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.
setNodeMetadata calldata builder for publishing your multiaddrs.