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 README
First release / API preview

Builder 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.

Control and packet flow

Control call IP / packet path

Containing app

Flutter engine lives here

Your PacketTunnel extension

Embedded and signed by your app

Swift + Go
Transport socket
Shipped in the SDK {"type":"udp","options":{}}
App process Stage 1 of 5

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 project
import "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.

Read the schema and defaults
Standard UDP configuration
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, and peers. 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.
Build against the current contract

This first release is an API preview. Follow the repository for the current builder and Dart integration; their public surface may change.