Skip to content
NEAXCE — a pure Swift and C, Xray-compatible VPN engine for iOS and macOS

A Swift/C VPN engine for iOS and macOS that speaks Xray-compatible protocols straight through Apple's NEPacketTunnelProvider — without gomobile or an embedded Go core.


CI Swift 5.9+ Platforms License: Apache 2.0 Status: alpha


Why · Features · Install · Integrate · Transport status · Limits

Warning

Alpha. The engine has run on real iOS devices with VLESS+gRPC and VLESS+XHTTP. Reality and Shadowsocks are partial (see Transport status). The public API is not stable yet.

Why NEAXCE?

Apple runs a Packet Tunnel in an app extension with a hard memory ceiling — historically 15 MB, raised to at most 50 MB on recent iOS. Cross the line and the system kills the tunnel process. To the user that reads as "the VPN keeps dropping every few minutes under real traffic."

Most iOS Xray clients embed xray-core through gomobile, and that is where the budget goes:

  • the Go runtime reserves a large slice of resident memory before a single byte of user traffic flows;
  • Go's garbage collector spikes under bursty load and pushes the extension past the ceiling;
  • connection state and DNS caches grow because the Go stack was never tuned for this sandbox.

NEAXCE takes Go out of the picture. Protocols and transports are plain Swift. The userspace TCP/IP stack that bridges the TUN device into per-flow streams is plain C (lwIP). The engine idles at a few MB and keeps headroom for dozens of concurrent connections inside the 50 MB budget — so the tunnel stays up and flows stop tearing.

Features

🧠 Memory-first by design
Idles at a few MB, with room for dozens of flows inside the 50 MB NE budget.

🔌 No Go, anywhere
Swift for protocols and transports, C (lwIP) for the stack. No gomobile, no Go runtime.

🕳️ No DNS leaks
DNS-over-TCP rides the active encrypted transport, on every transport including Reality.

🔀 Real Xray transports
VLESS over gRPC and XHTTP (HTTP/2 + TLS). Shadowsocks and Reality are landing.

🧱 Userspace TCP/IP
Vendored lwIP with a NAT-rewrite trick terminates TUN TCP flows in userspace.

🍎 Drop-in provider
Subclass one base class; three small files wire up a working tunnel.

Requirements

  • Swift 5.9+ to build the library. Running the test suite needs Swift 6 / Xcode 16 (the tests use swift-testing).
  • Apple platforms: iOS 15+, macOS 12+, tvOS 15+.
  • Entitlement: the host app needs com.apple.developer.networking.networkextension with the value packet-tunnel-provider. Apple grants it on request; it is not on by default.

Install

Add the package to your Packet Tunnel Extension target with Swift Package Manager:

// Package.swift
.package(url: "https://github.com/pinusmassoniana/NEAXCE.git", branch: "main")
.target(
    name: "MyPacketTunnel",
    dependencies: [
        .product(name: "NEAXCE", package: "NEAXCE")
    ]
)

Integrate in three files

The Examples/BasicIntegration folder has copy-paste reference code for all three.

1. Packet Tunnel extension entry point

Subclass NEAXCETunnelProvider and hand it your App Group. This is the only code the extension needs:

import NEAXCE

final class PacketTunnelProvider: NEAXCETunnelProvider {
    override var configuration: NEAXCEConfiguration {
        NEAXCEConfiguration(
            appGroupIdentifier: "group.com.yourcompany.YourVPNApp",
            loggerSubsystem: "com.yourcompany.vpn"
        )
    }
}

Set NSExtensionPrincipalClass in the extension's Info.plist to $(PRODUCT_MODULE_NAME).PacketTunnelProvider.

2. App-side manager

TunnelManager.swift is a NETunnelProviderManager wrapper that creates or loads the VPN profile, encodes a VPNConfig into providerConfiguration, and starts, stops, and observes status.

3. Feed a config

VPNConfig is the wire format. Build it by hand, or parse a subscription URI with the bundled URIParser.swift (handles vless:// and ss://).

let config = VPNConfig(
    serverHost: "vpn.example.com",
    serverPort: 443,
    serviceName: "vless-grpc",
    uuid: "4c4c6842-0000-0000-0000-000000000000",
    transportType: .grpc,
    path: nil
)
try await tunnelManager.connect(config: config)

Transport status

Transport Status Notes
VLESS + gRPC + TLS ✅ working Primary exercise path
VLESS + XHTTP + TLS ✅ working HTTP/2 POST+GET split
Shadowsocks (AEAD) 🚧 partial chacha20-ietf-poly1305, aes-256-gcm
Reality (XTLS) 🚧 partial Handshake + record layer; no XTLS flow

Tip

On .reality and .shadowsocks, DNS is tunnelled through the encrypted transport by default. Plaintext DNS forwarding is opt-in only, behind allowInsecureDNS — so DNS fails closed rather than leaking if no encrypted path exists.

What's in the box — layer-by-layer
Layer Component
TCP/IP stack lwIP (BSD-3, vendored) with a NAT-rewrite trick, so TUN TCP flows terminate in userspace
Protocols VLESS (full); Shadowsocks AEAD (chacha20-ietf-poly1305, aes-256-gcm)
Transports gRPC over HTTP/2+TLS; XHTTP over HTTP/2+TLS; Reality (TLS 1.3 ClientHello forgery, partial)
DNS DNS-over-TCP through the active transport (no leaks); optional opt-in forwarder
Provider NEAXCETunnelProvider : NEPacketTunnelProvider with path reassert, sleep/wake, optional App Group stats
Project layout
Sources/
├── CLwIP/                   Vendored lwIP + thin C helpers
│   ├── include/             Public headers (module.modulemap)
│   ├── core/                lwIP core (ipv4/, ipv6/)
│   ├── LwIPHelpers.c        Swift-facing C shims + NAT rewrite
│   └── sys_arch.c           NO_SYS=1 platform glue
└── NEAXCE/
    ├── Core/                Provider, engine, config, per-flow state
    ├── LwIP/                Swift wrapper over CLwIP
    ├── DNS/                 DNS proxies (VLESS / Shadowsocks / Reality / opt-in direct)
    ├── Crypto/              Reality handshake, TLS 1.3 primitives
    ├── Protocols/           VLESS + Shadowsocks codecs
    ├── Transports/          gRPC, XHTTP, Reality
    └── Logging/             Optional rolling file logger

Tests/NEAXCETests/           Unit tests (XCTest + swift-testing)
Examples/BasicIntegration/   Reference SwiftUI plumbing

Known limitations

  • Reality is partial: the handshake and TLS 1.3 record layer work, but the XTLS vision flow and connection migration are still open.
  • No HTTP/2 multiplexing. Each VLESS stream opens its own TLS connection; connection pooling is planned.
  • No UDP over gRPC/XHTTP. UDP from the TUN is dropped; the NAT-rewrite is TCP-only by design.
  • One skipped test (VLESSCodecTests.testVisionProcessorsHandleFragmentedInput) marks the vision-processor fragmentation gap. It is XCTSkip-ped with a reason so CI stays green, and stands as a visible TODO for the vision refactor.

Contributing

See CONTRIBUTING.md. Report security issues through SECURITY.md, not public issues — a bug here can leak traffic or expose a user's address.

License

Apache License 2.0. See LICENSE. Third-party terms, including lwIP's BSD-3-Clause, are in THIRD_PARTY_LICENSES.md.

About

Pure Swift + C (lwIP) VPN engine for iOS & macOS: runs Xray-compatible protocols inside Apple's Network Extension with no Go runtime, so it survives the 50 MB cap that kills gomobile-based xray-core clients. VLESS over gRPC/XHTTP working; Shadowsocks & Reality partial.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages