12 KiB
Subscriptions, Plugins, and Listener Configuration
Subscription information
See the SIP-008 subscription extension guide for field formats, the nested-group extension, and response requirements for subscription providers.
A SIP-008 JSON root may contain bytes_used and bytes_remaining (non-negative integers up to UInt64), with the custom fields expires_at (service expiration time) and traffic_reset_at (traffic reset time) inside the root-level x_shadowsocksx_ng object with schema_version: 1. Root-level date fields are ignored. A missing or invalid namespace, or a missing or unsupported version, provides no date information; standard traffic fields remain independently available. Date values use RFC 3339 strings; an omitted field or null means no value was provided. An invalid item is ignored without affecting valid server or group information.
A successful refresh replaces all allowlisted subscription information. Omitted or invalid fields clear their previous values; a failure or cancellation preserves the last successful information and its separate success timestamp. Information and the catalog record are saved atomically in the same document. The app does not separately save the raw response or a plaintext snapshot of server data containing passwords. Passwords and plugin parameters continue to use Keychain storage. If an older catalog file has no success timestamp, the app does not infer one.
Subscription cards show valid information returned by the provider. A usage progress indicator appears only when both traffic values are valid and the total is greater than zero. Dates use the user's current language and time zone. Expiration and reset information do not change proxy start, stop, or activation behavior.
User plugin catalog
PluginCatalog combines the current managed manifest with user records from the local plugins.json; names are case-sensitive. A user mapping takes precedence. If an override is invalid, the app does not fall back to the managed program; deleting the override restores the app's current default. A file-read error remains a blocking fact and is not treated as an empty mapping. User paths, local launch arguments, and plugin transport modes are not included in server information, subscriptions, or share files. User paths are also excluded from diagnostic reports.
The Plugins section at the bottom of Settings shows built-in and user plugins together. It displays all names for each record and supports adding records, editing their name sets and paths, overriding built-ins by name, deleting records, and restoring a same-named built-in. User and built-in records can use the tcp_only or tcp_and_udp plugin transport mode. The default is tcp_only; generated sslocal configuration omits plugin_mode in that case. The app does not probe whether a plugin supports SIP003u.
In the built-in editor, Clear parameters clears only the draft launch arguments and preserves the transport mode; the change takes effect when saved. Names and launch arguments are edited as lists. Launch arguments preserve their order and complete strings. Paths can be entered manually or selected from a file chooser. When reading fails, the UI hides the entries and offers a read-only retry; plugin management has no global reset. At the composition root, mapping commits and the explicit repair interface are wired into the form, activation, and diagnostics. After a successful save, the app refreshes activation candidates and converges asynchronously; a runtime failure does not undo the save. The app does not monitor files for replacement. Each plugins.json v1 entry migrates independently into a user record with persistent identity; the next save writes v2, with no merging by path. The existing built-in program is available as both v2ray-plugin and v2ray-plugin-v1. See ADR-0033.
MacOSPluginInspection reads only download-quarantine, signature, and system-assessment facts. It does not launch user plugins or change system policy. A failed or inapplicable check is unknown; a policy assessment that denies execution does not prove that execution is actually prohibited and does not block saving a mapping or activating it. Hostless test doubles do not prove that macOS will block a plugin in practice.
Managed Cloak plugin
The app includes the official darwin-arm64 ck-client from Cloak v2.12.0. Servers refer to it by the name cloak or cloak-v2; both names share one built-in record. ck-client is the packaged filename, not a reference name. A same-named user plugin still takes precedence, and an invalid override does not fall back automatically to the built-in program.
Server plugin options are passed upstream unchanged; the app does not validate Cloak option semantics. Options may be semicolon-separated Key=value arguments or an absolute path to an external JSON configuration file. The user maintains an external file; the app does not copy it or manage its credentials or lifecycle. Shared paths do not include file contents, so recipients must provide the corresponding file themselves. Local launch arguments are stored separately and are not shared with server information.
The configuration parser at the pinned version has these limitations: a string is recognized as an argument list only if it contains both ; and =, so a single Key=value is treated as a file path; escaped semicolons are still split; quotes and backslashes cannot be reliably passed using generic SIP003 escaping; and an empty segment stops further parsing. For complex values, maintain a JSON file yourself. Provide UID, PublicKey, ServerName, and EncryptionMethod; set NumConn=4 explicitly if needed. Do not rely on the README's stated default of 4: the pinned implementation disables connection reuse when this value is omitted. Explicit endpoint fields override SIP003 environment variables, so usually omit LocalHost, LocalPort, RemoteHost, and RemotePort.
The default plugin transport mode is tcp_only. Upstream UDP=true switches to a UDP listener; it does not indicate SIP003u support for carrying TCP and UDP at the same time. The app still lets users set the existing transport mode; this integration is verified for TCP configuration only. Cloak's GPL-3.0 license, source, and third-party notices are bundled at Contents/Resources/ThirdParty/Cloak/; see the NOTICE there for source and build entry points.
Local and LAN listeners
While the background proxy client is running, local SOCKS and HTTP always listen on 127.0.0.1, using ports 11086 and 11087 by default. Local SOCKS always enables UDP relay. The LAN entry point is disabled by default and uses separate ports 11088 and 11089. It supports IPv4, dual-stack, and IPv6-only modes. All four ports must be in 1025...65535 and must be distinct.
UDP relay for LAN SOCKS and authentication shared by SOCKS/HTTP have independent settings. The username and password are stored together in the encrypted listener configuration; Keychain stores only the listener-specific master key. At runtime, the app derives an authentication file specific to the current user. Passwords in upstream authentication-failure logs are redacted. Settings check TCP port use only and do not configure a separate UDP port. The home-page command uses a local or LAN port according to the selected address. When LAN authentication is enabled, it includes encoded credentials; the tooltip shows the full command.
Listener settings use the standard age file format and a separate X25519 identity. They are encrypted in proxy-listening.age and contain only local and LAN listener values. Proxy mode, the default rule action, proxy-service enablement, system-proxy enablement, and additional system-proxy exceptions are stored separately in UserDefaults under keys prefixed with proxyControl.. An in-memory snapshot of runtime calculations is not a unified persistent document. Listener settings are still jointly validated and saved atomically; UserDefaults preferences do not report or roll back a failed synchronized disk write.
The app does not migrate the old settings.json. Missing values in the new listener file and added preferences use defaults, so existing development configuration must be set again. The old file is not read or deleted, and legacy Keychain entries are not accessed or cleaned up. Existing independent preferences such as language, silent launch, and test URL, as well as server information, remain unchanged. A listener-read failure blocks normal startup, including silent launch, and shows an explanatory alert with only the Reset listener configuration action. After the user confirms, the app creates a new listener master key and atomically saves default settings, with LAN listening disabled. Until reset succeeds, the old file and any existing background proxy or system proxy are preserved. After success, the app converges using the defaults and the original enablement intent. The app does not migrate old proxy-listening.enc or proxy-listening.json files and does not read or clean up old per-item Keychain credentials; if an old file is found, the user is likewise asked to confirm a reset. See ADR-0034, ADR-0040, ADR-0041, ADR-0042, and ADR-0050.
Application Support directory
The root directory is ~/Library/Application Support/ShadowsocksX-NG2/, organized by lifecycle:
| Subdirectory | Files and purpose |
|---|---|
configuration/ |
catalog.age, proxy-listening.age, activation.json, custom-rules.json, and plugins.json: persistent information managed by the GUI |
runtime/ |
Supervision data, runtime receipts, failure records, PID, stable links, and generation-named configuration, GUI-private intent summaries, authentication, and ACL files |
logs/ |
agent.log, retained after the background proxy stops |
runtime-generation, runtime.lock, and agent.lock are in runtime/ and remain after service shutdown. Do not implement stopping the service by deleting the whole runtime directory. Directory permissions are 0700; regular-file permissions are 0600. Runtime configuration and authentication files may still contain plaintext secrets. UserDefaults, Keychain, LaunchAgent plists, and bundled rules and plugins keep their respective locations.
This layout change does not provide automatic migration. Files under the old root are not read, moved, or deleted, including the initial silent-launch.json; saved UserDefaults preferences are unchanged. Before switching a development environment, quit the GUI and stop the old Agent and sslocal. After confirming that the processes have exited, move the five GUI files listed above to configuration/ unchanged if you need to keep them; do not move runtime artifacts. Current age files can reuse the Keychain identity stored under the service for the same user. Historical custom .enc files are not migrated and cannot be reused by only changing their extension. Do not assume that copying ciphertext to another machine will make it decryptable. Set directory permissions to 0700 and file permissions to 0600, then launch the new build so it can derive runtime artifacts at the new paths. If the files are not moved, the new directory initializes on first use and does not read anything from the old root. The rejection rules for old formats inside the new directory still apply.
Path rules are centralized in ApplicationSupportPaths and RuntimePaths. In Agent tests, SSXNG_CONTRACT_PATH takes precedence over SSXNG_RUNTIME_DIR, and all paths for one deployment are derived from the directory containing that contract. If only the runtime directory is set, the standard configuration-link name is used. SSXNG_LOG_PATH can independently select the log location. If a test contract or runtime directory is set without a log path, logs remain in the overridden runtime directory so tests do not write to production logs.