github MotherofallVPNs/MoaV v2.2.0

6 hours ago

Monitoring stopped recording which user visited which site. If you have ever run the monitoring profile, please read the first section — there is data on your disk right now.

The rest of the release is a monitoring overhaul that fell out of that work — Snowflake now reports properly, every dashboard was reviewed, and moav doctor gained a host-health command — plus the fixes a real 2.1.0 → 2.2 upgrade turned up.

Keys, users and certificates are untouched.

Upgrade

cd /opt/moav
moav update
moav build snowflake     # 2.14.1, metrics endpoint baked in
moav start               # NOT `moav restart` — several images are rebuilt
moav doctor              # new host + net checks report on this box
moav net apply           # if doctor says the tuning file is from an older MoaV

moav update will offer new .env variables. Accepting the defaults is correct for almost everyone.


1. Security: your monitoring stack was keeping a browsing log

Two places recorded which client connected to which hostname.

The monitoring stack. MoaV shipped ghcr.io/zxh326/clash-exporter with -collectDest, which emits one metric series per (client IP, destination hostname) pair. Measured on one live server:

distinct hostnames 7,592
distinct client IPs 4,849
series kept 389,324
share of the whole database 83%

Retained 15 days. No dashboard ever read it — it was that exporter's default behaviour, and we did not notice.

sing-box's own log, on every server whether or not monitoring was enabled: [<username>] inbound connection to <host>:443, at the default log level.

What changed

  • clash-exporter is removed, not filtered — so re-adding a scrape job cannot bring it back. CLASH_EXPORTER_VERSION and IMAGE_CLASH_EXPORTER no longer do anything.
  • The metrics your dashboards used now come from MoaV's own sing-box exporter, which gained singbox_active_connections, singbox_version_info, and correct per-user country attribution.
  • Destinations are stripped from the sing-box log before anything is written. Username, client IP and connection id are kept — those drive per-user metrics; nothing needed the hostname. The pre-upgrade log is cleared once on start.
  • Per-user volume and liveness stay. The problem was the link between a user and a destination, not visibility. You still get username, bytes and country for quota and abuse handling.

Your data is still on disk

Nothing new is recorded from here on, and it expires with retention. To remove it now:

cd /opt/moav
docker compose stop prometheus
docker volume rm moav_moav_prometheus     # deletes ALL metrics history
docker compose up -d prometheus

Full policy and the reasoning: OPSEC → What MoaV Records. Audit and decisions: #297.

New: site analytics (opt-in, off by default)

To know what your proxy is used for without knowing who did what, set ENABLE_SITE_ANALYTICS=true:

  • domains only, folded to the registrable name (edge-42.example-cdn.netexample-cdn.net)
  • a domain is named only after at least 5 distinct clients reached it in the same bucket; below that it counts as other
  • ranked by how many people used a domain, not by bytes
  • counters advance once per hour, so the chart cannot be lined up against per-user connection counts
  • no client identifier is ever produced — not filtered afterwards, never created

Measured on a real server, that threshold drops 94% of domains while still accounting for ~89% of traffic, because the median domain is visited by exactly one person.

On a small server, be careful. With a handful of active users even aggregates leak, which is why this is opt-in. If too much lands under other, widen SITE_ANALYTICS_BUCKET_SECONDS — do not lower SITE_ANALYTICS_MIN_CLIENTS. The new Threshold Cost panel shows whether that would help.

ENABLE_SITE_ANALYTICS_RESEARCH=true also records destination port and protocol. Separate switch, because a rare port identifies far more than a popular domain.


2. Snowflake reports properly now

If you donate bandwidth through Tor Snowflake, the dashboard was misleading and is rebuilt.

  • Snowflake proxy → 2.14.1, which serves its own metrics endpoint. MoaV's log-parsing snowflake exporter is deleted — the numbers come straight from the proxy, so a restart reads as a counter reset instead of a cliff.
  • Per-country users served (#183). The proxy has exposed tor_snowflake_proxy_connections_total{country} since 2.11 and MoaV was not reading it. That needs a Tor-format GeoIP database, now fetched into its own volume (385,622 IPv4 + 276,646 IPv6 ranges) by a one-time tor-geoip-updater. Without it the proxy logs Error loading geoip db and labels every connection "".
  • The dashboard is rebuilt: people and countries served, rendezvous success rate, relay throughput, failed connections per hour, and bandwidth donated — each counted over the time range instead of as a flat lifetime total, and using max_over_time where the counter is too sparse for increase to see a country's first connection.

3. Every Grafana dashboard reviewed

Prompted by "the numbers look wrong". They were.

  • Totals silently reset. Counters restart with their container, and panels read them raw — one server showed Total Downloaded as 779 KiB while Downloaded over the same data read 31.5 MiB. Audited across all ten dashboards; all 219 queries re-checked against a live Prometheus.
  • Three separate bugs behind the sing-box country panels: connections counted once per poll instead of once per connection (42,428 vs a true count in the low hundreds), every user's country reported as XX, and "Users by Country" querying the connections metric.
  • Panels that ignored the time picker, and panels sampled at a resolution their data does not have.
  • One country now keeps one colour across time-series panels.
  • New site-analytics panels, in a collapsed row that stays out of the way when the flag is off.

4. moav doctor gained two checks

  • moav doctor host — CPU load against the core count, swap in use, zombie processes, and the busiest container (it names a bandwidth-donation container when that is what is saturating a small host). Working out why one server felt slow used to take four rounds of uptime, top, docker stats and a ps pipeline; this is that in one command.
  • moav doctor net answers its own question. Kernel packet-drop counters are since-boot and never reset, so after moav net apply fixed the cause the warning could never clear — and doctor told you to run nstat twice and compare by eye. It now samples for you and says which counters are still climbing.

5. Longer retention, now configurable

PROMETHEUS_RETENTION_TIME goes from a hardcoded 15 days to 90 days, with PROMETHEUS_RETENTION_SIZE (2 GB) as the real guard — whichever is reached first wins. A longer window is only affordable because the per-user destination series are gone: roughly 7,000 active series where there were 389,000. LOG_MAX_SIZE and LOG_MAX_FILES now control container log rotation.

6. Everything a live upgrade exposed

Found by upgrading real servers, not in CI:

  • moav update said nothing after changing docker-compose.yml, and suggested rebuilding a service that no longer exists
  • a bind-mounted config file kept its old contents after an update — a file mount follows the inode, and git replaces the file, so the container read the old copy forever (moav update now prints the --force-recreate it needs)
  • a setup-profile job added by a release never ran on upgrade, so Snowflake started with no GeoIP database
  • the GeoIP database was re-downloaded on every moav start; the build cache cap ignored the disk
  • moav doctor peers --fix died silently on any busy server, and when it did run the reset did not stick
  • CDN could be switched off by upgrading; a donated user could lose their whole bundle; the CDN WebSocket path rotated in silence
  • zombie processes in the admin and grafana containers
  • moav status listed one-shot plumbing (geoip-updater, bootstrap) as failures
  • exporter code changes never reached a running container — the code is baked into the image, so a fix silently did not apply without a rebuild
  • FUNDING.yml addresses are quoted, so a YAML parser stops truncating the ETH address

Project charts (star history, per-release test counts, translation coverage) are generated on a dedicated chart branch and embedded by URL, so nothing generated is committed to main.


Breaking

  • clash-exporter is removed. Any dashboard or alert you wrote against clash_* metrics stops returning data — the equivalents are singbox_*.
  • The Snowflake metric names changed. served_people / download_gb / upload_gb remain as deprecated aliases so your own panels do not go blank, but new panels should use snowflake_connections_total and snowflake_relayed_bytes_total. The dashboard's history does not carry over.
  • moav start, not moav restart. The sing-box image, the sing-box exporter and Snowflake are rebuilt, and a restart reuses the old image.

Thanks

Nearly everything here was found by running MoaV on live servers under real load, not by reading the code.

Full changelog: https://github.com/MotherofallVPNs/MoaV/blob/main/CHANGELOG.md

Full Changelog: v2.1.0...v2.2.0


Quick Install

curl -fsSL https://moav.sh/install.sh | bash

This will install MoaV to /opt/moav and guide you through setup.

Documentation

moav.sh/docs — full documentation

Get started

Reference

Understand it

Help out

Running it with an AI agent? llms.txt is a compact
orientation for coding agents; llms-full.txt is the
whole corpus. Both ship as release assets.

Community

Telegram · X · Issues · moav.sh

Don't miss a new MoaV release

NewReleases is sending notifications on new releases.