Open-source project
mbm110/MSN-GUARD avatar
mbm110/MSN-GUARD

msn-guard publishes its architecture and withholds the parameters that make it work

High-performance Rust-powered Android VPN — Full-device tunneling via SHARD (Custom Protocol), MASQUE/HTTP-3, WireGuard, WARP-on-WARP, Psiphon, and Tor.

532 stars83 forksKotlinAGPL-3.0

At a glance

What is it?
mbm110/MSN-GUARD is an Android client that puts every app on the phone through a TUN interface and offers five transport paths, with a Rust core compiled to a shared library, a Kotlin layer for lifecycle and interface, and a user-space TCP stack doing the terminating. The README is unusually explicit about its own architecture and unusually silent about its working parameters, on purpose, and the repository carries names the documentation never mentions.
Who is it for?
msn-guard is worth reading as engineering if you build anything that has to survive inspection on a hostile network, because the README is explicit about its own architecture, its own measurement discipline, and the exact things it will not tell you. It is not a recipe.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The README has a section explaining what it deliberately leaves out

This is the first thing to read, because it governs everything else in the document.

The README is written in Persian, with an English version at the repository root, and it contains a section titled roughly what is not written in this document. The list of omissions is specific: the connection strategy, which it splits into the handshake parameters, how gateways are selected and ranked, the order of the ladder, the time budgets, and those particular values that cause a session to survive active filtering.

It then says these details are the result of a long and expensive period of experimentation on real networks, and that they are intentionally not published. The document explains what the program does and why it has this shape. A ready-made filter bypass is not delivered.

The stated reason is worth quoting in spirit because it tells you how to read everything else: the useful life of a result in this area depends on how quickly it gets copied, and the things that really work are not written down.

So this article can describe the architecture, the five transport paths, the measurement discipline, and the defaults, and cannot describe how to make a session survive. That is not a gap in the writing. It is the project's design decision, stated in advance.

What it does claim as its own contribution is measurement rather than invention. The target has been Iranian networks, every protocol choice in the client came from real measurement on domestic operators rather than from guesswork or documentation, and the tie-breaker is stated as a rule: wherever a measurement result differed from the official documentation, the measurement won.

The repository description says SHARD, the core library says aether, and the tree has anytls

Three names for one project, and a directory the documentation never mentions.

The repository description leads with full-device tunnelling via a custom protocol it calls SHARD, alongside the five named paths: MASQUE over HTTP/3, WireGuard, WARP-on-WARP, Psiphon, and Tor.

The README describes five transport paths and SHARD is not among them. The Rust core, which the architecture section says compiles to a shared library named for a different word entirely, is where prober, account, quic, masque, wireguard, and netstack modules live. That library name appears nowhere in the description or in the project name.

And the top-level file listing includes a directory for a sixth protocol family that no visible section of the README mentions, alongside a second directory whose purpose is not described, the application module, the core module, a tools directory, and documentation.

The mismatch is not cosmetic. If you are auditing what ships, the set of network mechanisms is wider than the set the README enumerates, and neither the description nor the documentation reconciles the three names.

One more labelling point. The repository's primary language is recorded as Kotlin, and the description leads with the claim that it is Rust-powered. Both are true and the architecture section resolves it: the Rust core does the protocols and the encryption, the Kotlin layer owns the interface, the VPN lifecycle, and platform work, and a C layer runs the packet processing. So the badge and the tagline are each describing a different half of the codebase.

TCP is terminated in user space, then handed to a local SOCKS

The architecture section traces one outbound packet through six steps, and the third and fourth of them explain a lot about the design.

The user's application sends the packet. It enters Android's TUN interface, which the client creates through the platform's VPN service so that every TCP, UDP, and QUIC packet from every installed application goes through the tunnel rather than only the browser. Then a tun2socks stack paired with a userspace TCP stack terminates it in user space. Then it is handed to a local SOCKS listener on the loopback interface. Then the Rust core does the encryption and encapsulation. Then the packet goes to the upstream gateway and out to the internet.

So there is no kernel TCP here and no root requirement, which is why it runs on a stock phone, and the price is that TCP performance is bounded by userspace processing rather than the network stack.

UDP and QUIC take a different route. The C layer section describes a separate datagram bridge that moves UDP and QUIC traffic over loopback, which is what makes video, gaming, and voice calls work rather than degrading to TCP.

The Kotlin layer is six named files, and two of them matter beyond the obvious. One manages the native tun2socks process as a separate child. Another starts Tor, writes Tor's own configuration file, and owns the ladder of modes and dependent bridges. A third is the SOCKS front end for Tor, and it is also the place where DNS is handed to Tor's own DNS port rather than being resolved locally.

That is a meaningful privacy property and it is easy to miss: DNS for the whole device goes into Tor's resolver, not into the phone's resolver and not into the operator's.

Four defaults matter more than the protocol list

The feature table has twelve rows and four of them are the ones to read twice, because they are what makes a VPN on a hostile network safe to leave on.

The first is the emergency kill switch. If the tunnel drops, the client cuts the entire network rather than letting traffic fall back to the clear. That is the difference between a tunnel failing and a session becoming visible, and it is the single most important line in the table.

The second is forced DNS. Only a public resolver is used and the operator's resolver is left aside entirely. On a network where the resolver is the first thing inspected, that removes a leak that the tunnel itself would otherwise leave open.

The third is the verified connection. The indicator does not report success until real traffic has actually passed. This is not a nicety in this project, because the README spends a paragraph on a failure mode where a gateway completes a clean handshake and then never carries your packets, and says reading that from the real log is genuinely hard. A success light that only lights on observed traffic is the mitigation.

The fourth is split tunnelling, letting you choose which applications stay outside the tunnel, which is how you keep a banking app or a push-notification channel working.

Around those, the ordinary conveniences: one-button connect with gateway selection, negotiation, and recovery all automatic; a live status display with exit address, country flag, data consumption, connection duration, and a running log; a Quick Settings tile so you do not have to open the app; and exit-country selection across 27 countries for Tor and 25 for Psiphon, each listed with its real relay and server count.

The primary path is QUIC, and its HTTP/2 fallback is called dead code

The first transport path is a CONNECT-IP encapsulation over QUIC, built on Cloudflare's QUIC library. The stated goal is that the traffic should not be distinguishable from an ordinary HTTPS connection, because that leaves no protocol fingerprint for deep packet inspection to key on.

The README then lists four points it considers worth making, and it frames them explicitly as things it is not going to explain how. This article reports that they exist and does not reconstruct them.

The first is about a header. The client sends a value for a pseudo-standard header that the real network edge accepts, and that value is not the one registered in the standard. The registered value is rejected. The README says the correct kind was found with a two-part test against a live edge and is documented nowhere public.

The second is that this is practically impossible over HTTP/2. The h2 path never advertises the capability this transport depends on, so negotiation dies before it starts, and only HTTP/3 answers. The README is blunt that the HTTP/2 fallback here is not a degraded mode but dead code, which is the kind of statement a project makes when it has stopped pretending to support something.

The third concerns the gateway order, and it is the operationally important one. The order of the initial gateways was measured rather than alphabetised. Most published addresses get their handshake rejected, only a small minority carry traffic, and the ranking is embedded in the build rather than discovered at run time. So the list is a compiled-in artefact with an expiry date, and there is no mechanism described for refreshing it.

The fourth explains the verified-connection feature from the other side: the gateway to use comes from one specific registration response, and a second response exists that looks equally valid and is wrong. Taking the wrong one produces a clean handshake to a gateway that will never carry your packets.

A short, fixed startup budget governs the whole sequence, on the reasoning that a gateway which answered once and then went silent is stuck rather than slow, so reconnecting beats waiting.

The WireGuard rule: the socket that authenticated must carry the traffic

The second path is direct WireGuard transport with a full Noise handshake, for networks that have not closed UDP. Where it works it is the fastest of the five, and for that reason it is tried first.

One rule on this path is described as expensive, which usually means it was learned the hard way. The same socket that passed validation must be the socket that carries the traffic. Validating a tunnel and then rebuilding it are not equivalent operations, because an operator can accept one flow and drop the next one that looks identical to it. The README says the program no longer does this, and that this is precisely what separates a real WireGuard connection from one that only completes a handshake.

That is the same failure shape as the wrong-gateway problem on the QUIC path, arrived at from a different direction. Both are cases where a visible success signal exists without traffic flowing, and both are why the verified-connection feature exists.

The third path is a WireGuard tunnel inside another WireGuard tunnel. The use case is stated precisely: it helps where the outer route is reachable but the inner endpoint is not, which on some operators is the actual situation, and where a WireGuard path already exists the support is cheap.

The fourth and fifth paths, Psiphon and Tor, run their own upstream cores and connect into the same TUN through local SOCKS rather than being implemented in the Rust core. Tor is described as usable on its own or carried inside the QUIC tunnel, the second case being for a network that has blocked Tor itself. Psiphon is run through a three-step ladder ordered by measured real connection time on the hostile operator, and the README is in the middle of explaining how that order differs from Psiphon's own defaults when the visible text stops.

Android only, no root, and no server in the middle

The scope is narrow and clearly stated.

This is a native Android client, and the difference it claims over proxy applications is the level at which it works: those generally cover only the browser, while this creates a TUN interface through Android's own VPN service, so every packet from every installed application passes through the tunnel with no per-app configuration.

Two consequences follow from that choice and are worth stating plainly. It is Android only, and it needs no root, because the TCP stack runs in user space rather than in the kernel.

The routing is also direct. The README says no intermediate server from the maintainers sits in the middle, and that the phone speaks straight to the upstream gateway. That removes the operator-run infrastructure question entirely, and it also means the only party who could be compelled to hand over a log is the gateway operator.

The delivery side is where this project is least conventional. The repository's homepage field points at a chat channel rather than a website, there is no documentation site in the repository, and the visible README contains no installation step at all. It describes what the program does and why, and it links the repository's releases for the artefacts. If you are looking for an install command, this README does not have one.

The remaining files are conventional for a project of this shape: a licence, a security policy, a contributing guide, a design document, Gradle build files with a wrapper for both platforms, and three shell scripts whose names suggest they drive continuous integration.

AGPL, a master branch, and two tags four and a half hours apart

The administrative details, which are worth having before you look at anything else.

The licence is AGPL version 3, which for a network client that people will modify and redistribute is the strict end of the scale and a deliberate choice for this kind of project. The default branch is called master. The last push was on 2026-10-01.

The three most recent releases are a two-point zero, a two-point zero point one, and a two-point one. The first two are on the same day, 2026-09-21, about four and a half hours apart: 2.0.0 published at 05:44 and 2.0.1 at 10:06. Then 2.1.0 on 2026-09-26. A patch release four hours after a major is a fast response to something, though the release notes are not in the visible text.

The documentation has an English version and a Persian original, and the Persian one is what the platform indexes, which means English-language searchers will mostly find the older-looking one unless they follow the language switch at the top. The language link sits beside the Persian label at the top of the document, next to a badge row that includes an in-page anchor for the transport-path section.

That anchor is worth a note. It is a percent-encoded Persian string rather than an ASCII fragment, which is a fragile thing to depend on: any tooling that rewrites headings, or any change to the section title, breaks the link, and no ASCII anchor is offered alongside it.

The badge row also links the project's actions page and its releases, which together with the chat channel as the declared homepage tells you most of what there is to know about how this project distributes itself.

Editorial conclusion

msn-guard is worth reading as engineering if you build anything that has to survive inspection on a hostile network, because the README is explicit about its own architecture, its own measurement discipline, and the exact things it will not tell you. It is not a recipe. The project says so directly: the handshake parameters, the gateway selection and ranking, the ladder order, the time budgets, and the values that make a session survive active filtering are deliberately unpublished, on the stated grounds that the useful life of such a result depends on how fast it gets copied. Three things to know before going further. The defaults are the right ones for this problem: a full-device TUN, a kill switch that cuts the whole network when the tunnel drops, forced public DNS, split tunnelling, and an indicator that refuses to report success until real traffic has passed. The gateway ranking is compiled into the binary rather than discovered at run time, so it is a list with an expiry date and no refresh path. And nothing visible here describes how to install it: the documentation covers architecture and paths, the artefacts live in the repository's releases, and the project's own link is a chat channel.

Frequently asked questions

What does MSN-GUARD actually do?

It is a native Android client that creates a TUN interface through the platform's VPN service, so every TCP, UDP, and QUIC packet from every installed app passes through the tunnel rather than only the browser. It offers five transport paths: MASQUE over HTTP/3, WireGuard, WARP-on-WARP, Psiphon, and Tor.

Does MSN-GUARD route traffic through a server run by its authors?

No. The README states that no intermediate server from the maintainers is involved and that the phone communicates directly with the upstream gateway. Psiphon and Tor run their own upstream cores and connect into the same TUN through local SOCKS.

Does the MSN-GUARD documentation explain how to bypass filtering?

No, and it says that deliberately. The handshake parameters, how gateways are selected and ranked, the ladder order, the time budgets, and the values that let a session survive active filtering are all withheld on purpose. The document explains what the program does and why it has this shape, and states plainly that a ready-made bypass is not delivered.

How is the MSN-GUARD codebase split?

Three layers. A Kotlin layer handles the interface, the VPN lifecycle, TUN creation, the Quick Settings tile, and the native tun2socks process, and it starts Tor and writes Tor's own configuration. A Rust core compiled to a shared library handles gateway probing and ranking, device registration and certificates, QUIC, MASQUE encapsulation, WireGuard, and the packet bridge. A C layer runs a tun2socks stack with lwIP to terminate TCP in user space plus a datagram bridge for UDP and QUIC.

What happens if the MSN-GUARD tunnel drops?

An emergency kill switch cuts the whole network rather than letting traffic fall back to the clear, and DNS is forced through a public resolver so the operator's resolver is never used. The connection indicator also refuses to report success until real traffic has actually passed, which matters because the README describes a failure mode where a gateway completes a clean handshake and never carries your packets.

Official sources

  1. License: AGPL-3.0
  2. mbm110/MSN-GUARD on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mbm110-msn-guard.svg)](https://hysenlabs.com/projects/mbm110-msn-guard)