singbox_ffi 0.1.4
singbox_ffi: ^0.1.4 copied to clipboard
Flutter FFI bindings and platform packaging for embedding the sing-box libbox core from Dart.
singbox_ffi #
Flutter/Dart FFI bindings for a small native wrapper around
github.com/sagernet/sing-box/experimental/libbox.
Use this package when a Flutter app needs to embed a sing-box core through FFI: validate sing-box JSON configuration, start a local proxy service, reload it, stop it, and query the native core version.
This package is intentionally only the Dart/Flutter plugin plus native packaging scaffolding. The large native libraries are distributed separately through this project's GitHub Releases.
What It Can Do #
- Load a bundled or explicitly provided
singboxffinative library. - Query the sing-box/libbox version and Go runtime version.
- Initialize libbox with app paths, locale, command server options, logging limits, debug mode, and OOM options.
- Validate a sing-box JSON config before starting it.
- Start a sing-box service from JSON config and receive an opaque handle.
- Reload a running service with new JSON config.
- Stop and free a running service handle.
- Drain or stream service logs for UI log panels.
- Enable and restore the desktop system proxy for local proxy inbounds.
- Expose the same ABI to C, Dart raw FFI bindings, and a small Dart wrapper.
The wrapper currently supports proxy-style sing-box configs such as local
mixed, SOCKS, or HTTP inbounds with normal sing-box outbounds and routing.
Current Limits #
These platform integrations are not implemented by this wrapper yet:
- TUN mode (
OpenTunreturns an unsupported error). - General event draining APIs beyond logs.
- SSH agent, platform shell, SFTP, user lookup, and connection-owner lookup.
If your app needs those features, treat this package as a lower-level starting point rather than a complete sing-box GUI runtime.
Install #
Add the package:
dependencies:
singbox_ffi: ^0.1.3
Then add the native artifact for each platform you build. The pub.dev package does not include native binaries.
| Platform | Expected artifact location |
|---|---|
| Windows | windows/artifacts/x64/singboxffi.dll |
| Linux | linux/artifacts/x86_64/libsingboxffi.so |
| macOS | macos/Libraries/libsingboxffi.dylib, macos/Libraries/libsingboxffi.a, or a vendored framework/xcframework |
| Android | android/src/main/jniLibs/<abi>/libsingboxffi.so |
| iOS | ios/Libraries/libsingboxffi.a or a vendored framework/xcframework |
Download prebuilt artifacts from the matching GitHub Release, or build them
locally from this repository. Desktop CMake files fail fast when a required
desktop artifact is missing so the problem is visible during flutter build.
Quick Start #
import 'package:singbox_ffi/singbox_ffi.dart';
const configJson = '''
{
"log": {"level": "info"},
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 2080
}
],
"outbounds": [
{"type": "direct", "tag": "direct"}
],
"route": {"final": "direct"}
}
''';
Future<void> main() async {
final singbox = SingboxFfi.openBundled();
print('sing-box: ${singbox.version()}');
print('Go: ${singbox.goVersion()}');
singbox.init();
singbox.checkConfig(configJson);
final service = singbox.start(configJson);
try {
// The mixed proxy is now listening on 127.0.0.1:2080.
await for (final event in service.logs()) {
if (event.isReset) {
// Clear your visible log list.
} else {
print('[${event.levelName}] ${event.message}');
}
}
} finally {
service.close();
}
}
For Flutter apps, SingboxFfi.openBundled() is the recommended loader. It
matches the package's platform packaging:
- Use the explicit path when you pass one.
- On iOS, use
DynamicLibrary.process()because the native archive/framework is linked into the app. - On Android, Windows, Linux, and the default macOS flow, open the platform dynamic library from normal package/executable search paths.
For command-line smoke tests or custom packaging, pass a path explicitly:
final singbox = SingboxFfi.openBundled(r'C:\path\to\singboxffi.dll');
API Overview #
High-level Dart API:
final core = SingboxFfi.openBundled();
core.version();
core.goVersion();
core.init(SingboxInitOptions(...));
core.checkConfig(configJson);
final service = core.start(configJson);
service.state();
core.systemProxyStatus();
service.enableSystemProxy();
service.logs();
service.drainLogs();
service.clearLogs();
service.disableSystemProxy();
service.reload(nextConfigJson);
service.close();
Lower-level APIs are also exported for advanced integrations:
SingboxRawBindingsexposes typed Dart FFI function pointers.SingboxNativeSymbolscontains the exported native symbol names.SbInitOptionsand native typedefs mirror the C ABI.SingboxFfi.fromLibrary()lets you provide an already-openedDynamicLibrary.SingboxFfi.process()works only when the native symbols are already linked into the current process.
Initialization Options #
SingboxInitOptions maps directly to libbox setup options:
| Dart option | Meaning |
|---|---|
basePath |
Base directory used by libbox. |
workingPath |
Working directory for runtime state. |
tempPath |
Temporary directory for runtime files. |
locale |
Optional locale passed to libbox before setup. |
commandSecret |
Secret for libbox command server support. |
commandPort |
Command server listen port. Use 0 for automatic/default behavior. |
logMaxLines |
Maximum log lines retained by libbox internals. |
debug |
Enable libbox debug setup. |
oomKillerEnabled |
Enable libbox OOM killer behavior. |
oomKillerDisabled |
Disable libbox OOM killer behavior. Defaults to true. |
oomMemoryLimit |
Optional OOM memory limit. |
Native ABI #
The native library exports a small C ABI:
typedef uint64_t sb_handle;
char *sb_version(void);
char *sb_go_version(void);
void sb_free_string(char *ptr);
int32_t sb_init(const sb_init_options *opts, char **err_out);
int32_t sb_check_config(char *config_json, char **err_out);
int32_t sb_start(char *config_json, sb_handle *out, char **err_out);
int32_t sb_reload(sb_handle handle, char *config_json, char **err_out);
int32_t sb_stop(sb_handle handle, char **err_out);
int32_t sb_free_handle(sb_handle handle);
int32_t sb_drain_logs(sb_handle handle, int32_t max_entries,
char **json_out, char **err_out);
int32_t sb_clear_logs(sb_handle handle, char **err_out);
int32_t sb_service_state(sb_handle handle, char **json_out, char **err_out);
int32_t sb_system_proxy_status(char **json_out, char **err_out);
int32_t sb_set_system_proxy(const char *host, int32_t port,
const char *bypass, bool enabled,
char **err_out);
Strings returned by the core must be released with sb_free_string. Handles
returned by sb_start should be stopped with sb_stop and released with
sb_free_handle. The Dart SingboxService.close() helper does both.
sb_drain_logs returns a JSON array of log events. A reset event means libbox
cleared its internal log history, usually because a service started or reloaded.
Dart callers normally use SingboxService.logs() instead of parsing this JSON
directly.
sb_service_state returns a JSON state snapshot for a running handle. Dart
callers normally use SingboxService.state().
sb_system_proxy_status and sb_set_system_proxy wrap the platform system
proxy helper used by libbox's system proxy callbacks. Dart callers normally use
SingboxFfi.systemProxyStatus(), SingboxFfi.setSystemProxy(), or
SingboxService.enableSystemProxy().
System proxy support is implemented for Windows, macOS, and Linux desktop environments with GSettings. Other platforms return an unsupported error.
Build Native Artifacts Locally #
Windows with MSYS2 UCRT64 GCC:
$env:CGO_ENABLED = "1"
$env:CC = "C:\msys64\ucrt64\bin\gcc.exe"
$env:PATH = "C:\msys64\ucrt64\bin;$env:PATH"
go build -trimpath -buildmode=c-shared `
-tags "with_gvisor,with_quic,with_wireguard,with_utls,with_naive_outbound,with_purego,with_clash_api,badlinkname,tfogo_checklinkname0" `
-ldflags "-s -w -buildid= -checklinkname=0" `
-o build\singboxffi.dll .
Static archive builds use the same ABI:
go build -trimpath -buildmode=c-archive `
-tags "with_gvisor,with_quic,with_wireguard,with_utls,with_naive_outbound,with_purego,with_clash_api,badlinkname,tfogo_checklinkname0" `
-ldflags "-s -w -buildid= -checklinkname=0" `
-o build\singboxffi.a .
Static/process loading is for apps that link the native symbols into the app binary or Flutter runner. It is not a general fallback for a missing dynamic library.
Run The Smoke Proxy #
The repository includes a Dart smoke example that starts a real local mixed
proxy on 127.0.0.1:2080.
flutter pub get
cd examples\flutter
flutter pub get
dart run bin\proxy.dart ..\..\build\singboxffi.dll
In another terminal:
curl.exe -x socks5h://127.0.0.1:2080 https://example.com
Press Ctrl+C in the Dart process to stop the proxy.
Repository Notes #
- The repository root is the publishable Flutter FFI plugin.
example/is the pub.dev-facing example.examples/flutterandexamples/care repository smoke tests.- Release automation builds native artifacts and attaches complete packages to GitHub Releases.
- The lightweight pub.dev package excludes generated native binaries.
License Notice #
This project links against github.com/sagernet/sing-box/experimental/libbox.
sing-box is distributed under the GNU General Public License, version 3 or
later, with the additional upstream naming restriction copied in
LICENSE.sing-box.
Distributions of this wrapper, linked binaries, and applications embedding the produced native library should carry the corresponding GPL notice and must not use the sing-box name or imply association with the upstream application without prior consent.