Flutter plugin + Apple Network Extension SDK
Flutter control.
Native packet path.
A tunnel for the app you own, with a transport you can replace.
WGC NE connects your Flutter app to its own PacketTunnel extension. sing-box handles IP flows in Go. WGC handles authenticated WireGuard packets.
Read the project READMEBuilder and Dart APIs may change. Use the architecture document for the current contract.
See where your transport plugs in.
Select a stage to inspect the boundary.
Containing app
Flutter engine lives here
Your PacketTunnel extension
Embedded and signed by your app
Swift + Go{"type":"udp","options":{}}
Flutter owns the controls.
The plugin controls an owned NETunnelProviderManager through native code and reads status through provider messages. Packet data does not pass through the Flutter engine.
Outbound direction shown. This is an interactive architecture schematic; it sends no network traffic.
The transport boundary
Three methods.
Your packet carrier.
The Go core depends on WGC v0.2.0. Its transport interface exchanges WireGuard messages with remote endpoints.
Only standard UDP ships. Your private authentication, relaying, framing, or obfuscation belongs in a wrapper compiled into your Go core.
Explore the WGC projectimport "net/netip"
type Transport interface {
ReadFrom(b []byte) (int, netip.AddrPort, error)
WriteTo(b []byte, addr netip.AddrPort) (int, error)
Close() error
}
- Concurrency
- One reader and concurrent writers.
- Buffers
- Do not retain them after a method returns.
- Lifecycle
- Close unblocks pending I/O.
Register custom factories in your extension’s core. Flutter selects a registered name and JSON options; unknown names fail closed.
Configuration version 1
An explicit contract, in JSON.
{
"version": 1,
"private_key": "<canonical Base64 X25519 private key>",
"addresses": ["10.7.0.2/32"],
"peers": [{
"id": "exit",
"public_key": "<canonical Base64 X25519 public key>",
"endpoint": "203.0.113.10:51820"
}],
"default_peer": "exit",
"transport": {"type": "udp", "options": {}},
"mtu": 1280,
"dns_server": "1.1.1.1:53",
"network": {
"dns_servers": ["198.18.0.2"],
"included_routes": ["0.0.0.0/0"],
"excluded_routes": []
}
}
An example from the architecture document. Replace the key placeholders and use addresses allowed by your remote WireGuard server.
- Required inputs
version,private_key,addresses, andpeers. Endpoints and resolvers must be numeric and unzoned.- Platform owns the routes
- NetworkExtension installs network settings. The core adds each peer’s outer endpoint host route to the exclusions.
- Credentials stay on device
- Configuration goes into a device-only Keychain item in your shared access group. Preferences hold a profile reference, not private keys or full configuration.
sing-box, with a defined scope.
The default policy sends TUN TCP/UDP and DNS through the selected peer. Raw ICMP forwarding is outside the userspace outbound’s stated scope.
Built-in registrations in this SDK
- TUN
- direct
- WGC
- UDP/TCP DNS
- FakeIP DNS
Integration ownership
Your app ships
its own extension.
Share the SDK and native ABI. Each containing app embeds and signs its own PacketTunnel target and keeps its own VPN identity.
- Containing app
- Own the UI, accounts, API integrations, and policy. Use the Flutter plugin to manage your native profile and read status.
- PacketTunnel target
- Run Swift and Go, without a Flutter engine. Link your chosen
WGCCore.xcframework; set the NetworkExtension and matching shared Keychain entitlements. - Custom Go core
- Compile private transport factories and wrappers into this binary. A transport name in JSON selects code already registered in your build.
This first release is an API preview. Follow the repository for the current builder and Dart integration; their public surface may change.