github getmeridian/meridian v4.0.0b9
Meridian 4.0.0b9 (beta)

pre-release4 hours ago

Warning

This is a beta of Meridian 4. Nobody gets it automatically: pip, uv, the install script, and meridian update on 3.x all skip it. Your 3.x servers keep working.

Use a separate, fresh server you can wipe. Meridian 3.x and 4 cannot share a server: both need port 443. Meridian 4 refuses to deploy onto a server that still runs 3.x; replacing a 3.x server means tearing it down first, and every one of its users needs a new link.

Try it next to 3.x

You need uv (macOS or Linux; on Windows use WSL). On your computer Meridian 4 keeps its state in ~/.config/meridian, so your 3.x command and ~/.meridian stay untouched:

curl -LsSf https://astral.sh/uv/install.sh | sh   # installs uv and uvx; open a new terminal afterwards
alias meridian4='uvx --prerelease allow --from meridian-vpn@latest meridian'
meridian4 deploy 198.51.100.10        # IP of the separate server

When the deploy finishes, meridian4 panel prints the address of the web panel, where you manage people and API tokens from a browser, and a one-time link to set its password. Treat both like passwords.

Prefer questions to flags? meridian4 setup walks you through one server or several (exits in other countries, relays) in English, Russian, Persian, or Chinese. The alias always runs the newest beta; after a new one comes out, meridian4 apply brings your servers up to date.

Already running an earlier 4.0 beta? Those deployed Remnawave; b9 replaces it with Meridian's own control service. Run meridian4 migrate once: it keeps every client link, key, limit, expiry, and traffic count, and checks the links are unchanged before it removes Remnawave. It first backs up the panel's database to ~/.config/meridian/backups/ on your computer and to /var/backups/meridian-migration/ on the control server; there is no rollback, the backup is the way back. It resumes if interrupted. Panel-only data is not carried over (the panel's admin account, per-day usage history, HWID device lists, plugins, and client IP forwarding), and the review lists it.

Keep ~/.config/meridian. It is how Meridian 4 manages your servers. After every change, each computer that manages the fleet saves an encrypted snapshot of the control service, and your first deploy shows a recovery key once: write it down and keep it offline. If the control server is lost or seized, meridian4 restore --to NEW-SERVER rebuilds it from the newest snapshot (on a bare IP, every link changes and is printed; with a domain they stay). See the recovery guide. When 4.0 is released, install meridian the usual way and it manages the same servers; nothing to move or redeploy. Set up with an earlier alias that pointed at ~/.meridian-v4? Drop that part; Meridian finds the folder on its own.

Then, from a device inside the censored network, run:

meridian4 test 198.51.100.10          # does real traffic pass from here?
meridian4 test --probe 198.51.100.10  # does the server look like a VPN server from here?

Known gaps: the control server cannot be moved with node replace, only rebuilt with restore; client IP forwarding and per-user IP limits are not available (the control service stores no client addresses); and a server you remove should still answer, or its cleanup cannot finish: if it is gone, add --forget-unreachable to let Meridian forget it.

Tell us what you see

Open a Meridian 4 beta report. The most useful details: your country and network type (home ISP or mobile operator), whether your client app connects, and the output of meridian4 test; for deploy problems add meridian4 doctor. People you shared a connection page with can open "Not connecting?" on it, run the check, and send you the copied report: it contains no links or addresses. Outputs hide secrets, but replace IPs, domains, and links before posting.


Warning

Это бета-версия Meridian 4. Она не устанавливается сама: pip, uv, установочный скрипт и meridian update в 3.x её пропускают. Ваши серверы на 3.x продолжают работать.

Используйте отдельный новый сервер, который не жалко переустановить. Meridian 3.x и 4 не могут работать на одном сервере: обоим нужен порт 443. Meridian 4 откажется разворачиваться на сервере, где ещё работает 3.x; чтобы заменить такой сервер, его сначала нужно очистить, и всем его пользователям понадобится новая ссылка.

Как попробовать рядом с 3.x

Нужен uv (macOS или Linux; на Windows через WSL). На вашем компьютере Meridian 4 хранит своё состояние в ~/.config/meridian, поэтому команда 3.x и папка ~/.meridian останутся как есть:

curl -LsSf https://astral.sh/uv/install.sh | sh   # ставит uv и uvx; затем откройте новый терминал
alias meridian4='uvx --prerelease allow --from meridian-vpn@latest meridian'
meridian4 deploy 198.51.100.10        # IP отдельного сервера

Когда развёртывание закончится, meridian4 panel покажет адрес веб-панели, где в браузере управляют людьми и API-токенами, и одноразовую ссылку для задания пароля. Относитесь к обоим как к паролям.

Удобнее отвечать на вопросы, чем писать флаги? meridian4 setup проведёт через настройку одного сервера или нескольких (выходы в других странах, реле) на русском, английском, персидском или китайском. Алиас всегда запускает самую свежую бету; после выхода новой meridian4 apply обновит ваши серверы.

Уже запускали раннюю бету 4.0? Она разворачивала Remnawave, а b9 заменяет его собственным сервисом управления Meridian. Выполните один раз meridian4 migrate: он сохраняет все ссылки клиентов, ключи, лимиты, сроки и счётчики трафика и проверяет, что ссылки не изменились, прежде чем удалить Remnawave. Сначала он делает резервную копию базы панели в ~/.config/meridian/backups/ на вашем компьютере и в /var/backups/meridian-migration/ на управляющем сервере; отката нет, вернуться можно только из копии. После прерывания команда продолжает с того же места. Данные, которые есть только в панели (учётная запись администратора, посуточная история трафика, списки устройств HWID, плагины и передача IP клиентов), не переносятся, и обзор их перечисляет.

Не удаляйте ~/.config/meridian. Через эту папку Meridian 4 управляет вашими серверами. После каждого изменения каждый компьютер, управляющий флотом, сохраняет зашифрованный снимок сервиса управления, а первое развёртывание один раз показывает ключ восстановления: запишите его и храните офлайн. Если управляющий сервер потерян или изъят, meridian4 restore --to НОВЫЙ-СЕРВЕР восстановит его из свежего снимка (при адресе-IP все ссылки меняются и выводятся на экран; с доменом остаются прежними). См. руководство по восстановлению. Когда выйдет 4.0, установите meridian обычным способом, и он будет управлять теми же серверами: ничего переносить и разворачивать заново не нужно. Настраивали раньше с алиасом, указывавшим на ~/.meridian-v4? Уберите эту часть: Meridian найдёт папку сам.

Затем с устройства внутри сети с блокировками выполните:

meridian4 test 198.51.100.10          # проходит ли реальный трафик отсюда?
meridian4 test --probe 198.51.100.10  # похож ли сервер на VPN-сервер, если смотреть отсюда?

Известные ограничения: управляющий сервер нельзя перенести командой node replace, только восстановить через restore; передача IP клиентов и лимиты по IP недоступны (сервис управления не хранит адреса клиентов); удаляемый сервер должен отвечать, иначе его очистка не завершится: если сервера больше нет, добавьте --forget-unreachable, и Meridian его забудет.

Расскажите, что получилось

Откройте отчёт о бете Meridian 4. Важнее всего: страна и тип сети (домашний провайдер или мобильный оператор), подключается ли клиентское приложение, и вывод meridian4 test; при проблемах с развёртыванием добавьте meridian4 doctor. Те, с кем вы поделились страницей подключения, могут открыть на ней «Не подключается?», запустить проверку и прислать вам скопированный отчёт: в нём нет ссылок и адресов. Секреты в выводе скрыты, но перед публикацией замените IP-адреса, домены и ссылки.

[4.0.0] - Unreleased

Meridian 4 is a new foundation. Its own control service and web management
panel replace 3x-ui; one reviewed topology in ~/.config/meridian/cluster.yml
describes your whole fleet; a small agent on every server applies it; and no
server needs Docker. The first section lists what changed since the previous
beta, 4.0.0b8; the second is what 4.0 changes compared with 3.x.

Since 4.0.0b8

Read first: moving a Remnawave fleet

  • meridian migrate moves a fleet deployed by an earlier 4.0 beta onto the
    control service in place, keeping every link. It backs up the panel's
    database to your machine and to the control server, carries each client over
    with their keys, subscription ID, status, limits, expiry, and traffic,
    replaces each node's container with the agent, checks that every
    subscription link is unchanged, and only then removes Remnawave. It is
    resumable and has no rollback; the backup is the way back. meridian deploy
    offers it when it finds a Remnawave fleet.
  • Panel-only data is not carried over, and the review lists it: the panel's
    admin account and API tokens, per-day usage history, HWID device lists,
    panel plugins, response rules Meridian didn't set, the Remnawave
    subscription page, and client IP forwarding (it served the panel's per-user
    IP limits).

Web panel

  • Meridian runs its own control service in place of Remnawave: no Docker, no
    third-party panel, and every server runs a small agent (netagent, static
    builds for x86-64 and ARM64) that dials out to it. Nodes open no ports for
    management.
  • meridian panel prints the web panel's address and, while it has no owner, a
    one-time setup link (--setup-link resets the owner: new password, sign-in
    code off, every device signed out). There is no panel password.
  • In the panel you manage people, API tokens, privacy levels, the fleet
    notice, and the default page language; servers, revisions, and the
    subscription title are read-only there.
  • Person and password forms ask Leave/Stay while edits exist, including
    browser back. Person status and usage refresh while visible without
    replacing drafts, and actions update the screen they came from and name the
    person in their feedback.
  • Expiry forms edit a native date and time, show the browser zone and the
    exact UTC instant, and offer +1 month, +3 months, and No expiry.
  • QR images export as PNG or SVG with the person's name and fleet title, and
    supported browsers copy the PNG.
  • A closed Individual connections section lists each named connection with
    copy and QR actions and the Xray JSON, Mihomo, and sing-box URLs; client show --connections prints the same list.
  • Phone dialogs scroll with sticky actions and 44 px touch targets, and the
    panel keeps English and shows a retry notice when a language file fails to
    load.
  • Token creation blocks dismissal while it writes and keeps the one-time secret
    in its own step. People and Servers refresh while visible, mark stale data,
    and offer retry; background polling no longer keeps an idle owner signed in.

Connection pages and apps

  • Connection pages are served by the control service at every person's
    subscription URL, including people added in the panel; apps keep receiving
    their configs from the same URL. Old UUID links redirect, and a reviewed
    apply removes SSH-published page files and retires their service workers.
  • Owners choose the default language for connection and support pages through
    fleet language, the API, or Settings, and can override it per person
    (client set --language). The default follows each person's browser; saved
    page choices win, and app status messages stay English.
  • Native subscription status remarks come in English, Russian, Persian, and
    Chinese; status codes stay English.
  • Owners can preview, publish, expire, and clear one fleet notice
    (fleet notice or Settings): apps get it in an announce header and pages
    show a banner.
  • Reviewed owner contact (fleet contact) appears on connection and support
    pages in all four languages as a link that opens only when clicked, and
    supplies apps' support button.
  • An app too old for Meridian's connections (v2rayNG before 1.9.17) or no
    longer maintained (desktop NekoRay) gets one extra inert entry at the end of
    its subscription asking to update or switch to Throne. It is read from that
    request's User-Agent only and nothing is stored.
  • The Not connecting? section explains in all four languages that a red or
    "n/a" ping in an app does not mean the connection is broken, how to refresh
    the subscription now, and to turn on update-on-launch.
  • Setup follows your chosen platform and app: labelled store and direct-install
    alternatives (including desktop architectures and Linux packages), manual or
    one-tap import, and localized Connect steps, with TUN guidance on Clash
    desktop apps. Stash joins the additional iOS apps.
  • Pages show account status, current and lifetime traffic, expiry, and
    freshness in all four languages, with a marked offline answer; long
    connection lists start with three entries and a "Show all" toggle.
  • Pages validate configuration and app catalogs before rendering, open setup
    even when the optional app catalog stalls, keep quotes in links from breaking
    the HTML, offer selectable text when a copy fails, and tell search engines
    not to index them.
  • Xray JSON subscriptions keep Automatic first and add a named profile for each
    primary connection, with the same routing and fingerprint alternatives.
  • Subscriptions accept /v2ray-json, /stash, and /clash beside the
    existing formats; /sub/<token>/info returns account status and links;
    profile-update-interval follows delivery.update_interval_hours (1-168,
    default 6); subscription-refill-date carries the next quota reset.
  • Empty or unsupported-only subscriptions show inert "No connections available
    yet" entries instead of empty documents, duplicate connection names get
    stable suffixes, and Xray subscriptions explain when only fingerprint copies
    remain.
  • meridian fleet routing reads or reviews the client policy: ru-split
    (default) or full. Private ranges stay direct either way, and Xray JSON
    keeps its best-effort BitTorrent direct rule.
  • client preview and Settings explain which format an app's request gets,
    which protocols it includes, and which entries are omitted, without showing
    credentials.

Control service and agents

  • One service (ctld, running meridian serve) keeps users, traffic, and
    applied revisions in one SQLite file, serves every subscription format, logs
    no requests, and sits behind nginx on a secret path.
  • Agents enroll over SSH with a single-use token, verify every document the
    service signs, and run Xray. meridian apply upgrades an agent in place when
    Meridian ships a new build, keeping the node's identity.
  • meridian fleet history lists every reviewed apply the service recorded, with
    author, status, and plan hash; the apply lease lives in the service.
  • API tokens carry read, propose, or approve scopes, or a separate people
    scope for managing people without access to servers, backups, or settings.
    Tokens can expire after 1-3650 days (new panel tokens default to 90).
    Scoped integrations fetch OpenAPI 3.1 at /api/openapi.json.
  • Signed node commands are queued durably, delivered only to agents that accept
    their kind, executed once even if redelivered, and reported per node.
  • meridian node restart NAME and the panel's Restart Xray button restart a
    node's core and wait for readiness; --all restarts every managed unit.
  • client remove and client reissue offer --cut-off to end existing
    sessions once the old credentials are revoked. The confirmation discloses
    other people's reconnects, and results distinguish success, fallback
    restart, failure, and timeout.
  • client extend NAME --days N adds time after the later of now and the current
    expiry; client add creates limits, reset period, and device caps together
    with the account.
  • client set and the panel keep a private note and one tag per person
    (--tag family is saved as FAMILY); client list --tag filters.
  • CLI edits for enable, disable, limits, expiry, resets, and devices accept
    people created in the panel, using the same exact-name rules as client show.
  • client list --attention and /api/attention find people expiring within
    seven days, above 80% of their quota, or unused for three days, plus offline
    nodes and unhealthy reports; nothing about attention is stored.
  • fleet events and /api/events give a local timeline of lifecycle changes,
    enforcement, node outages, and token activity, without credentials or client
    addresses: up to 2,000 events for up to 180 days.
  • meridian server set records monthly bandwidth allowances, reset days,
    provider labels, and repeating payment dates, shown by fleet status, server
    lists and exports, the panel, and the attention API. Usage is approximate
    accepted upload plus download.
  • Node health reports Xray uptime apart from host uptime, traffic-collection
    failures, API failures, memory, disk, and load; fleet status and check flag
    unhealthy nodes and treat stale reports as unavailable evidence.
  • Node totals survive late counter epochs; people show first reported use and
    their last reporting node; fleet status shows who was active in the last
    report.
  • An apply after a Meridian upgrade no longer re-runs every server's package and
    hardening baseline; only a change to the baseline or the server's connection
    does.
  • meridian fleet upgrade covers the control service, node agents, and Realm.
    After meridian update it moves the service and agents to the new build;
    --tested skips GitHub, so Realm stays on the tested version.

Privacy and backups

  • Device limits and daily usage history are off by default. meridian privacy show|set and Settings disclose what each level stores and what a seized
    control server reveals; lowering a level deletes its records in the same
    transaction, and applies leave privacy settings alone.
  • Optional daily usage gives per-person and per-server totals per UTC day,
    kept 90 days by default (7-730), with client usage, fleet usage, and
    charts in the panel.
  • Optional device limits use private fingerprints or full details, a fleet
    default cap, and per-person overrides (client add|set --devices, client devices --remove|--clear). Apps without a supported HWID header are not
    counted, and no client addresses are stored.
  • Every machine that manages the fleet pulls an encrypted snapshot after each
    change, sealed to its own key and to a recovery key shown once at the first
    deploy. meridian backup pull, recovery-key, devices, and revoke
    manage them. Snapshots carry people, notes, tags, the notice, language
    choices, and privacy settings; private device records clear after a restore.
  • meridian restore --to SERVER rebuilds a lost or seized control server from a
    snapshot with a new certificate authority and signing keys, so the old server
    signs nothing your nodes accept. Domain links survive once DNS moves;
    bare-IP links change and are printed. On a machine with only the snapshot and
    the recovery key, restore brings the fleet files back too.

CLI

  • The version and beta banner prints once a day and after an upgrade, not before
    every command.
  • Console and CLI quotas use binary GiB and TiB with an explicit Unlimited, and
    expiries from 2099 on are refused.
  • Fleet import reviews name each person's changed creation settings instead of
    reporting a reorder.
  • Restart and cut-off commands report service failures in their JSON error
    envelopes, and a failed result read marks the node failed instead of a
    retryable timeout.

Docs

  • The Russian text of the CLI, web panel, connection and support pages, website,
    and docs was rewritten with one glossary.
  • Hosting, relay, and security guides in all four languages cover October 2026
    hosting reports, TLS transfer blocking, mobile whitelist limits, broken
    relay-to-exit routes, and an audited account of outbound requests and
    download verification.
  • The website and README show screenshots of the connection page and the web
    panel, and /demo/console/ is a clickable panel demo beside the connection
    page demo, both running on one invented fleet.
  • The README, website, docs, CLI, web panel, and connection pages say VPN
    where they mean the product and call the owner's console the web panel in
    all four languages; "proxy" stays only where it is technically exact.

Fixed

  • meridian apply no longer retries the removal of a deleted server on every
    run after an interrupted apply, and an exit whose first apply failed is
    cleaned up when you remove it (--forget-unreachable when the server is
    gone).
  • fleet status reports pending convergence separately from runtime health
    instead of calling a fresh but unapplied configuration healthy.
  • A second netagent run exits clearly instead of racing the running agent, and
    core starts wait for the Xray API and restore the previous configuration if it
    never comes up.
  • Mihomo, Clash, and Stash subscriptions quote a Reality short ID that looks like
    a number (such as 12e34567), which those apps read as a float and then
    refused the whole document.
  • API-created IPv6 endpoints produce share links with exactly one pair of
    brackets.
  • Removing a person erases their traffic totals and replay baselines as well as
    daily history.
  • client show includes lifetime traffic and the current period's start, also
    when daily history is off.
  • check reports background enforcement and blob mirror failures with safe error
    codes, and a lost token reply keeps its recovery notice.
  • A server that already ran nginx from nginx.org gets Meridian's sites included,
    so certificates and connection pages work there.
  • Hardening from a security review: the agent re-checks every name that reaches
    a file path, an Xray argument, or its API address; the service answers probes
    the way nginx does, takes small bodies before sign-in, drops traffic reports
    for unknown users, re-checks a node's session before handing it a document, and
    keeps its files private.

Removed

  • Remnawave, its database, Valkey, the subscription page container, and Docker
    on new fleets.
  • Meridian Studio, meridian studio, the local Engine behind them, and the
    studio install extra; the web panel is where fleet management lives.
    meridian deploy --json, --events=jsonl, --request, and --dry-run are
    unchanged for scripts and agents.
  • meridian panel --show-password (there is no panel password),
    deploy --client-ips (the control service does not forward client IPs yet),
    and client show --repair-page (pages are live at the subscription URL).

Compared with 3.x

Read first: this is not an in-place upgrade

  • Meridian 4 cannot manage servers deployed by 3.x, and existing client links
    cannot carry over. Your 3.x servers keep working.
  • To keep managing them, stay on 3.x: uv tool install 'meridian-vpn<4'.
  • To move: deploy a fresh server with 4.0, give your users the new links, then
    meridian teardown the old server (it also removes 3x-ui).
  • 3.x and 4.0 cannot share a server, since both need port 443. 4.0 refuses to
    deploy onto a server that still runs 3.x (or 2.x) instead of taking it over.
  • Requires Python 3.11+.
  • Both versions fit on one computer — 4.0 keeps its state in
    ~/.config/meridian (or $XDG_CONFIG_HOME/meridian) and leaves 3.x state in
    ~/.meridian alone. A fleet set up with a 4.0 beta in ~/.meridian-v4 or
    ~/.meridian is found there, so the release manages it without moving files.

Managing people

  • Your edits stick — Meridian owns only each person's existence, name, and
    squads. Disabling someone (client disable or the web panel), traffic limits,
    expiry, notes, and tags survive every apply. Disabled, limited, or expired
    people don't mark fleet status as degraded.
  • Connect a bot safely — the client guide shows how to give a community bot
    its own read-scoped, expiring API token instead of sharing Meridian's
    full-access one.

Deploy

  • Several fleets on one computer — meridian context list|use|rename|remove
    keeps separate fleets apart, say family and work, kubectl-style:
    meridian deploy IP --context work starts one, --context NAME (before or
    after any command) or MERIDIAN_CONTEXT picks one for a run, and
    context use changes the current one. An existing fleet becomes the
    default context without moving a file; others live in contexts/NAME/
    with their own saved servers, sharing Meridian's SSH key. A server belongs
    to one fleet at a time, context remove deletes only local files (asking
    first while the fleet has servers), and MERIDIAN_HOME still pins one fleet.
  • One command, verified end to end — meridian deploy IP sets up the control
    service and an exit, picks a camouflage target the server can actually use,
    pushes real traffic through the delivered subscription, and only then prints
    "Done" with a shareable connection page and QR code. Re-running it resumes or
    redeploys.
  • A setup wizard anyone can follow — meridian setup is a guided,
    resumable wizard for one server or many (exits, Realm relays, routing
    gateways with failover). It speaks English, Russian, Persian, and Chinese
    (asked first, remembered, or --lang), explains each term in one plain
    sentence, accepts local for the machine you are on, skips routing questions
    unless you have a gateway, suggests five short server names (astra, boba,
    foxtrot…), and ends with a plain summary before anything
    changes. Running meridian alone in a terminal offers to start it.
    Choices are arrow-key lists and checklists (Space toggles, ? explains,
    Esc goes back), typed answers are checked as you enter them, and the
    camouflage list leads with sites found in the exit's own network.
    MERIDIAN_PLAIN_PROMPTS=1 keeps the numbered prompts.
  • Names in any script — setup and client add take people's names as
    typed (Биба, مریم, 王小明); connection pages and output show them, and the
    service gets a Latin username (biba). Other client commands accept either.
  • Review before change — meridian plan shows what meridian apply would do
    (exit 0 converged, 1 config error, 2 changes pending, 3 evidence unavailable).
  • Paths — Reality, XHTTP, and Hysteria2 on UDP/443 by default; --domain
    adds WSS (Cloudflare CDN fallback). Blocking Russian destinations stays on
    by default (--no-geo-block to turn it off).
  • Camouflage next door — without --sni, a first deploy (and node add)
    scans the server's own subnet and prefers a site whose DNS really points
    there and that passes Reality's TLS 1.3 + HTTP/2 check, so the SNI and the
    server's IP look like they belong together. VPSes that get a /32 address
    (Hetzner Cloud and others) scan their surrounding /24. Well-known targets
    remain the fallback, and real traffic still decides.
  • Your own domain as camouflage (self-steal) — --self-steal on deploy,
    node add, and node sni makes Reality imitate a domain you own, pointed
    at the server with a DNS-only record, instead of someone else's site. The
    exit gets a Let's Encrypt certificate for the name and answers probes and
    unknown SNIs itself with nginx's stock 404, so the name, the certificate,
    and the address all match, and no outside site can go down or change under
    you. With --domain alone the one domain also keeps XHTTP and WSS working
    behind it. Meridian first checks where the name resolves from the server:
    it stops when it doesn't resolve and warns when it points elsewhere (a
    Cloudflare-proxied record). The wizard offers it as "My own domain", and
    test --probe no longer treats a self-steal name as an independent site to
    compare against.
  • Client IPs are never stored — the control service records no client
    addresses or connection logs; every TCP connection reaches Xray through nginx
    on the server.
  • TLS fingerprint is a setting — every Reality, XHTTP, and WSS link presents
    the chrome fingerprint by default (XHTTP and WSS used to send none, which
    exposed the client library); --fingerprint or the topology's path
    fingerprint switches it when a region starts flagging one.
  • Shrink a running fleet — meridian node remove and meridian relay remove
    edit the topology, show every resource they will delete, and clean up the
    node's agent and Xray, Realm units, firewall rules, and published endpoints.
    meridian plan lists removals too, and the review covers them.
  • Move an exit to another server — meridian node replace EXIT SERVER
    keeps the exit's ID, Reality keys, protocol paths, SNI, WARP and geo-block
    settings, and relays, so clients keep working, then cleans up the old server
    through the reviewed apply. The control server can't be moved this way; use
    meridian restore.
  • Forget a server that no longer answers — when a server a change removes
    doesn't answer over SSH (blocked from your network, or deleted at the
    provider), its cleanup failed on every apply. node replace, node remove,
    relay remove, and apply now offer to forget what was deployed there, or do
    it with --forget-unreachable; --yes alone never does. The control service
    still deletes what it held, and the server keeps serving the users it had until
    you delete or wipe it.
  • Change a camouflage SNI in one command — meridian node sni EXIT NEW_SNI
    and meridian relay sni RELAY NEW_SNI switch an exit or relay to another
    site when meridian test --probe says the SNI is blocked. They check that
    the exit reaches the site over TLS 1.3 with HTTP/2, show the review, and
    apply it. The old name is replaced, not kept alongside; apps switch at their
    next subscription refresh (every 6 hours) or a manual refresh. They, node add --sni, and relay add --sni refuse Apple names and www.microsoft.com.
  • Edit the fleet as a file — meridian fleet export > fleet.yml prints the
    topology as YAML with saved server names and no keys, credentials, links, or
    transport paths. meridian fleet import fleet.yml shows each change
    (+ client carol, ~ exit-2 sni www.example.com -> www.example.org), then
    applies the same review apply would; an unedited file is a no-op. Removed
    clients go as with client remove. --dry-run only shows the changes, and
    import refuses unsaved servers and edits that need their own command (moving
    an exit or the control server).
  • Pins stay current between releases — a weekly job opens a pull request
    raising the Realm, RealiTLScanner, and acme.sh defaults; the local Xray test
    client is pinned by checksum too, and a Realm tarball without a pinned
    checksum is refused instead of installed unverified. Relays get Realm's static
    build, which runs on any glibc, and a new binary replaces the running one only
    after it starts on that server.
  • One health command: meridian check — checks whatever the server is in
    your fleet: pre-flight readiness for a new server; the control service, each
    agent, every compiled port, TLS, and disk for control, exit, and gateway
    servers; and each Realm hop for relays. With no server it checks the whole
    fleet, and --json returns one typed report. meridian test --probe (short
    form: meridian probe) shows a server the way a censor's scanner would.
    preflight, rage, node check, and relay check are gone: each now stops
    with a one-line pointer to the new command (exit 2, or a JSON error envelope
    with --json).
  • meridian logs SERVER — shows a server's recent logs without logging in:
    the agent and Xray, the control service, and nginx on control, exit, and
    gateway servers, and the Realm units on relays. --grep TEXT keeps matching
    lines, --lines N sets how many, and --component picks node, panel, nginx,
    or relay. Every line is redacted like meridian doctor (tokens, keys, UUIDs,
    and secret paths), so the output is safe to share.
  • cluster.yml stops growing with every apply — each apply kept a full copy
    of every resource binding, so a small fleet's file passed 300 KB after a few
    dozen changes. Older copies of the same remote resource are now dropped when a
    change commits; the next apply shrinks existing files.
  • meridian plan no longer calls pending changes unavailable — after any
    topology edit (even one new client) it listed clients and endpoints as
    "observation failed" and exited 3, because their dependencies only get
    their new identities during apply. It now shows them as recheck … after
    those dependencies and exits 2, as for any pending change.
  • "What is blocked?" — meridian test --probe now tells a blocked server
    IP from a blocked camouflage SNI by comparing the server with and without the
    SNI against the real camouflage site, and inconclusive stays inconclusive.
    It also catches throttling: a ~50 KB download from the control server (and
    through the CDN when WSS uses one) that freezes after about 16–24 KB is
    flagged with a suggestion to change the IP. meridian check runs the
    download check too when it's run from another machine.
  • meridian test says how each path fails — every delivered path reports
    its time to first byte, and a failed one is reported as "connects, but no
    traffic passes" or "unreachable". --json adds data.transports.
  • meridian test says what to do about a blocked exit — when nothing on
    an exit answers from your network, it prints the meridian node replace
    command for that exit (--json: data.recovery). A blocked control server
    points to the recovery guide instead, since it can't be moved yet.
  • Name the server first, like ssh — every server command takes one target:
    an IP, a saved name, user@host:port, or local. --user and --ssh-port
    default to the saved values (else root and 22) everywhere, including
    deploy, node add, relay add, and server add. The old --server NAME
    option stops with a pointer to the positional target.
  • relay deploy is now relay add, matching node add, client add, and
    server add. The old name stops with a pointer to the new one.
  • Run Meridian on the server itself — after meridian deploy local (or on
    any saved server whose IP is on this machine), later commands such as
    client add, doctor, and node add run locally with sudo instead of
    SSHing back into the same machine. The same fleet copied to a laptop keeps
    using SSH.
  • Servers that came with only a password — when a server refuses your SSH
    keys but accepts a password, meridian deploy and meridian setup ask
    whether to create a key for Meridian and install it. Say yes, type the
    password once (hidden, never saved), and the deploy continues; the key is
    saved with the server for later commands. Without a terminal, with --yes,
    or with --json, they stop with the ssh-copy-id command instead.
  • Per-domain routes on an exit — meridian route add EXIT --via warp|direct|block VALUES... sends chosen domains, geosite: lists, or
    geoip: ranges through Cloudflare WARP, directly, or nowhere, ahead of the
    exit's default egress (for example, AI services that block datacenter IPs).
    route list and route remove complete it; the wizard keeps existing routes.
  • One route for AI services — meridian route add EXIT --preset ai sends
    ChatGPT, Claude, Gemini, Cursor, and the rest of the AI site list through
    WARP as one route named ai, so they never see the exit's hosting address
    (--via direct keeps them on it). The deploy guide adds what a server route
    cannot fix: changing exit countries, device-side leaks, and Google's
    country-association request.
  • Gateway rules can send traffic directly — a routing-gateway rule can
    send matching traffic out of the gateway itself instead of to an exit or
    pool, so Russian sites can leave from a gateway in Russia. The wizard offers
    "directly from the gateway" next to your exits and "nowhere"; in a topology
    file it is "action": "direct". The setup summary lists each gateway rule
    with where it goes, and existing gateways compile unchanged.
  • Gateway rules match zones and site lists — a domain rule takes a bare
    zone such as ru, su, or рф (every site under it), and a new site-list
    rule takes geosite: names such as category-ru, the same lists the Xray
    JSON and Mihomo subscriptions send directly. Both match the site name, so
    the gateway needs no lookup. Exit routes (meridian route add) take zones
    too.
  • Relays look like nothing in particular — nothing a hosting provider can
    list on a relay (service, binary, config path, firewall comment) names
    Meridian, Realm, a relay, or a VPN.
  • meridian check measures each relay leg — every hop downloads the exit's
    camouflage page through the next hop with the clients' SNI and reports
    connect time, first byte, and bytes received; a freeze in the 8–32 KB
    throttling window suggests moving the relay to another provider.
  • meridian test tests the whole fleet — with no target and several saved
    servers it tests every exit and relay entry in turn instead of asking which
    one, and exits with the worst result. Each path is named like its
    subscription entry ("Reality · Chrome", "XHTTP · Firefox", "Hysteria2 · UDP").
  • With several exits, subscriptions list the first server's entries ahead of
    later ones within each method instead of sorting them by exit name.
  • A brief network stall no longer fails the apply — every command shared one
    SSH connection, so a stall made each later command hang until its own timeout
    and the apply stopped. Now SSH notices a silent server within about 15 seconds,
    drops the stuck connection, and reconnects. Read-only checks and the control
    server's apply record retry for about a minute, and the progress shows
    "connection stalled, retrying…". A change whose outcome is unknown is checked
    on the server first and is never repeated blindly.

Sharing

  • Traffic limits and expiry from the CLI — meridian client set alice --limit 50GB --reset month --expires 2026-12-31 gives a client an
    allowance, a reset period, and an end date (--limit none and
    --expires never lift them); client add takes the same options, and
    client reset-traffic refills the allowance at once. client show prints
    "12.4 GB / 50 GB (resets monthly)" and the expiry, client list gains an
    Expires column, and applies leave all of it alone. Sizes are binary
    (1 GB = 1024³ bytes), and a bare date lasts to the end of that day, UTC.
  • meridian client reissue NAME replaces a leaked link — the client gets a
    new subscription ID and new keys, so the old subscription URL, connection
    page, and links stop working, and Meridian prints the new links. Status,
    limits, and expiry stay, and device registrations are cleared.
  • Happ, v2rayNG, and other JSON-capable apps get the Xray JSON — with it
    comes the automatic failover between entries; they used to get plain base64
    links. v2RayTun and Shadowrocket keep links.
  • Connection names show where traffic exits — entries lead with the exit's
    flag and country ("🇩🇪 DE · Reality · Chrome"). Deploy, node add, and the
    setup wizard look the country up once from the address's Regional Internet
    Registry; set --country when it is wrong (anycast or leased ranges).
  • A redesigned connection page — two clear steps (install an app, add the
    connection with one tap), the subscription QR beside them on a computer for
    setting up a phone, and everything else folded into quiet panels: every
    individual connection with Open, Copy, and QR, "Not connecting?" tips with
    the self-check, and the router guide. Russian pages no longer rename the
    first connection "Основной", and meridian dev preview shows a realistic
    page. Without --display-name, the header names the exit countries
    ("🇩🇪 Germany · 🇳🇱 Netherlands") in the page's language.
  • A WebRTC leak tip on the connection page — "Not connecting?" now says that
    an app running as a proxy only can let websites in a browser see the real IP
    address, and to turn on the app's VPN (TUN) mode. Shown in English, Russian,
    Persian, and Chinese, with no external links.
  • Connection pages are built from each user's real subscription, so relays and
    every enabled protocol appear. Subscription links live under the
    connection-page path and never reveal the web panel's address.
  • Apps refresh subscriptions every 6 hours — every subscription sends
    profile-update-interval, so Happ, Clash Verge, and other apps that read it
    pick up new exits and relays within hours.
  • meridian client add|show|list|set|reset-traffic|remove|enable|disable.
    client remove deletes the user and their page; links stop working
    immediately.
  • Connection pages recommend apps per platform — Happ first, then
    FlClashX and Clash Mi (plus Throne on desktop), with a link to the router
    guide and a note on subscription refresh and app updates. v2rayNG and
    Streisand import links are fixed, Throne import is added, and the broken
    v2rayN button is gone. App icons ship once in a shared cached file, so
    each page loads about 70 KB less.
  • "Check my connection" — the page tests the device clock, whether data
    keeps flowing (the throttling freeze), and the subscription from the
    visitor's own network, then copies a report without links or secrets that
    they can send you.
  • Every deploy offers every method — without a domain, XHTTP
    and Hysteria2 now run on the server's IP address (Let's Encrypt certifies
    IPs), next to Reality, so a block on one method leaves the others. A domain
    carries XHTTP and adds WebSocket through the Cloudflare CDN. Hysteria2 is on
    by default (--no-hysteria2 turns it off), and the wizard only asks for an
    optional domain instead of a list of backup methods.
  • Hysteria2 is disguised — its UDP traffic is wrapped in salamander
    obfuscation, so censors see random packets instead of QUIC with a readable
    server name. Every subscription format carries the setting automatically.
  • Entries have readable names — apps show "Reality · Chrome", "XHTTP ·
    Firefox", "WebSocket · CDN", or "Hysteria2 · UDP" (plus the exit's name when
    you have several, and "via relay" for relay entries) instead of internal IDs.
  • Every direct TLS method comes in two fingerprints — Reality and XHTTP
    are offered with both a Chrome and a Firefox TLS fingerprint, so if
    censors block one fingerprint, the other entry keeps working. Automatic
    switching never mixes the two: Russian filtering reportedly stretches a
    2-minute freeze to 10 minutes when a new fingerprint arrives during it.
    Xray JSON apps get a separate "Automatic · Firefox" entry, Mihomo apps can
    pick the copies by hand, and sing-box apps (which can't keep them out of
    automatic switching) don't get them.
  • Russian sites work with the VPN on — Xray JSON and Mihomo subscriptions
    send local-network, .ru/.su/.рф, geosite:category-ru, and geoip:ru
    traffic directly and refuse foreign QUIC, while the exit's block stays as a
    safety net. Plain Base64 imports still need the app's own routing settings.
  • Torrents skip the VPN in Xray apps — Xray JSON subscriptions send
    recognized BitTorrent peer connections directly from the device, so a torrent
    client left running doesn't draw abuse complaints against the exit.
    Encrypted peers and UDP DHT/uTP may still use the tunnel; Mihomo, sing-box, and
    Base64 subscriptions are unchanged.
  • sing-box apps get a Meridian profile — sing-box-based apps (SFA, SFI,
    Karing) pick the fastest working entry automatically, can still choose one
    by hand, and send local-network and .ru/.su/.рф sites directly. Nothing
    is downloaded at startup, so the app starts even when GitHub is blocked.
    sing-box has no XHTTP, so those entries are left out.

Docs and agents

  • Reviewed translations — the Russian, Persian, and Chinese CLI, docs, and
    connection pages were reviewed against the English source: mistranslations,
    machine-translation leftovers, and stray tokens fixed, one term per concept
    in each language, and docs claims checked against the code.
  • An agent guide — meridian help agents prints a Markdown guide that
    explains how Meridian works and how to drive it safely: non-interactive
    flags, JSON envelopes, plan before apply, exit codes, and which outputs are
    secrets. meridian help COMMAND shows any command's help, git-style.
  • Docs for people and agents — getmeridian.org's docs have search, a
    per-page table of contents, and proper right-to-left Persian. Every page is
    also served as Markdown (add .md to its address), with Copy page and Open
    in ChatGPT or Claude buttons, and /llms.txt indexes it all. Old /docs/...
    links redirect.
  • A page for the people you share with — the docs' new Connecting page
    walks a recipient through installing an app, adding the link, turning on
    VPN (TUN) mode, and what to do when it stops working; send it along with
    the link.
  • Lighter docs pages — diagrams arrive as images that follow the light or
    dark theme instead of ~3 MB of JavaScript, and docs pages prefetch a link
    only when you point at it instead of every page in the sidebar.
  • A lighter, readable website — the home page is fully translated into
    Russian, Persian, and Chinese and works without JavaScript (Tor Browser's
    Safest mode included); choosing English sticks. Fonts download about 70%
    less, text meets WCAG AA contrast in light and dark themes, link previews
    load in messengers, and the blog has an RSS feed at /blog/rss.xml. The
    command builder only offers commands and flags the CLI actually has.

Security and correctness

  • meridian test trusts valid certificates on macOS — uv's bundled Python
    looked for root certificates at a Linux path, so every Let's Encrypt
    certificate was reported untrusted; certificate checks now use certifi.
  • Starts with current Typer — Typer 0.26 stopped installing Click, and a
    fresh install of an earlier beta failed at launch with No module named 'click'. Meridian now needs Typer 0.26 or later and uses the Click it ships.
  • The installer can be verified — every release attaches install.sh and
    its SHA256SUMS, the installation guide shows how to check them, and the
    script pins uv's installer by version and SHA-256 instead of piping the
    latest one into a shell. MERIDIAN_VERSION=4.0.0 installs one exact release.
  • Servers look like ordinary hosts — units and directories don't name the
    proxy software: the control service is ctld in /opt/ctld, the agent is
    netagent, and its Xray is netcore, like relays' netfwd. meridian check
    and meridian doctor name the role behind each unit, and meridian teardown
    removes them.
  • Exits look up destinations over encrypted DNS only — the exit's direct
    egress used the host's resolver, so the hosting provider saw every site its
    users opened in plain-text DNS, and a failed encrypted lookup fell back to
    plain 8.8.8.8. Exits now resolve every name over DNS over HTTPS, asking
    Cloudflare, Quad9, and Google at once (each reached by IP address on IPv4 or
    IPv6), and never fall back to plain DNS: if none answers, sites by
    name fail through that server, and meridian check reports a resolver the
    server cannot reach. The system lab sniffs an exit during proxied traffic and
    requires no plain DNS question on the wire.
  • Large uploads over XHTTP never hit nginx's body limit — nginx's 1 MB
    default cut long uploads off on 3.x when a client chose a streaming XHTTP
    mode. 4.0 serves packet-up only and now also lifts the limit.
  • Removing or moving an exit cleans up its certificate — the acme.sh
    renewal, the certificate files, and (with the server's last certificate)
    the port-80 challenge listener go, instead of renewing forever.
  • A relay with its own camouflage site really imitates it — relay add --sni gave clients the new name while the exit answered probes with its own
    site's certificate (a www.google.com certificate for yandex.ru), which
    gives the relay away. Each relay name now gets its own Reality listener on
    the exit, and anything else sent through the relay reaches that site.
  • Debian 12 and 13 deploy — on Debian 12, fail2ban found no SSH log file and
    stopped right after starting; it now reads the system journal on every
    supported release, and a package Debian only remembers is no longer mistaken
    for an installed one. Stock Debian 13 deploys and joins as an exit too: the
    baseline no longer waits for a dnsutils package Debian stopped shipping.
  • node replace moves a bare-IP exit's certificate address — the moved
    exit kept the old server's IP as its XHTTP and Hysteria2 certificate name,
    so the new server could never get a certificate and the move failed. If a
    move already failed that way, rerun the same meridian node replace.
  • node add NAME keeps the saved name — adding a saved server without
    --name resaved it under its IP, so the name stopped working; it now keeps
    the name and uses it for the exit too.
  • meridian test NAME and meridian probe NAME take saved names — a name
    without a dot was refused as "not a full domain name".
  • Certificate failures say why — the error kept acme.sh's first lines
    (timestamps and progress) and cut off its reason; it now shows the last ones.
  • Unknown geo lists are refused at review — a gateway rule or exit override
    naming a geosite:/geoip: list or country the servers' Xray data lacks made
    Xray refuse its whole config, so the server stopped converging. Review now
    stops with the name and a close match (categry-ru → category-ru), and a
    saved fleet with such a name still loads so the rule can be removed.
  • Re-running a command resumes it — relay add for a relay that is already
    declared finishes it instead of failing on a duplicate listener, and a bare
    meridian test after removing servers tests what is left instead of asking
    which saved server to use.
  • Page branding is checked before anything changes — an unknown --color
    or an --icon that is neither an emoji nor a reachable image stops deploy
    with the valid choices; a redeploy that names only some branding flags keeps
    the rest.
  • No request logging on Meridian's web server; the live SSH port is never
    firewalled; teardown removes everything Meridian created and nothing it
    didn't: fail2ban stays on, and nginx keeps running when it serves your own
    sites, as do your own HAProxy/Caddy.
  • Deploy checks the loopback ports its nginx serves the control service on
    (8443 and 8444) before changing anything, and names the process holding one,
    instead of failing halfway through on nginx -t.
  • An out-of-date copy of cluster.yml can't undo newer changes. The control
    server records the last change applied, and apply refuses to run from an
    older copy or while another machine is applying.
  • Mihomo-based apps (FlClash, Clash Verge, ClashMi) and sing-box routers
    connect again. Recent Xray releases reject clients that announce a version
    below the server's minimum, and those apps announce 1.8.x, so their Reality
    connections failed with "authentication failed". Exits now accept any client
    that speaks Reality.
  • Setup no longer opens an untagged 443/udp firewall rule, and the next apply
    removes the one earlier v4 builds left.
  • Web panel and connection-page links typed or copied without the trailing slash
    now open; they used to redirect to an internal port and fail.
  • Subscriptions list Reality first, then XHTTP, WSS, and Hysteria2 last. Apps
    use the first entry by default, and Hysteria2 (UDP, often blocked) used to
    lead. sing-box and Stash no longer show a duplicate "automatic fallback"
    server.
  • XHTTP links work. Apps sending the default chrome fingerprint speak
    HTTP/2, and the server answered in HTTP/1.1, so no traffic passed.
  • A fresh deploy with Hysteria2 converges. Xray started before its certificate
    existed, so it refused to run and the deploy stopped.
  • Servers without apt (CentOS, Fedora, Alpine) are refused by name before
    anything changes, instead of failing halfway at "apt-get: command not found";
    meridian check reports them as a failure rather than a warning.
  • Reality keys are generated on your machine, so a fresh exit needs neither
    unzip nor an unverified Xray download to create them.
  • Adding or removing an exit renames existing subscription entries instead of
    leaving the old ones behind. Before, every entry appeared twice, and a stale
    Firefox copy slipped into the automatic failover group.
  • meridian check and meridian test are fail-closed: skipped evidence is never
    reported as a pass.
  • Mihomo and sing-box subscriptions are tested with the real engines, not only
    parsed: every change runs mihomo -t (Mihomo 1.19.32) and sing-box check
    (sing-box 1.14.3) on what the control service delivers. The client-management
    docs list the tested apps and engine versions.
  • Downloaded binaries (Xray, Realm, RealiTLScanner) are checksum-verified, so a
    re-pushed release cannot change what a fresh server runs.
  • meridian update stays on the current major version; --major moves to the
    next one after you have read its release notes.

Tested versions

Fresh deploys install the versions below. meridian fleet upgrade moves Realm
to its newest release in the same major without a Meridian update (--tested
keeps the version below), and the choice is saved for later applies.

Component Version
Xray-core (exits, gateways, test client) 26.7.28
Realm (relay forwarder) 2.9.6
RealiTLScanner (meridian scan) 0.2.3
acme.sh (certificates) 3.1.6

Not yet available

  • Client IP forwarding and per-user IP limits: the control service stores no
    client addresses. A fleet moved by meridian migrate loses it.
  • Moving the control server with meridian node replace; rebuild it from a
    snapshot with meridian restore.

Removed

  • 3x-ui, HAProxy and Caddy support; per-server proxy.yml credentials.
  • The website's ping page: a browser cannot tell a blocked server from one that
    refuses the connection. Run meridian test --probe from the censored network,
    or open your connection page there.
  • meridian fleet recover, apply --parallel/--prune-extras, and the
    desired_* lists in cluster.yml.

Don't miss a new meridian release

NewReleases is sending notifications on new releases.