mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-02 16:53:45 +00:00
新建与编辑共用名称校验,导入缺名时保存地址端口名称。 无供应商 ID 的订阅指纹仅包含地址和端口;密码、加密方式、插件及名称更新保留身份。未发版且无存量用户,直接启用新指纹,不迁移旧身份。 订阅刷新只清理新目录不再引用的旧凭据,保留复用的密码和插件参数。 验证:823 项 hostless arm64 测试、21 项 Python 检查及 Debug 构建通过;人工验收完成。
43 KiB
43 KiB
ShadowsocksX-NG domain context
Vocabulary
- Configuration catalog: The non-user-visible root of the 2.0 configuration tree. Its ordered top-level children may be server configurations or configuration groups.
- Server configuration: The user-visible connection data for one Shadowsocks server. It is a leaf with an opaque, persistent UUID, a source owner, and a required user-visible name.
- Configuration group: A named, ordered container that may contain server configurations and nested configuration groups. A node has at most one parent, and group membership forms an acyclic tree.
- Configuration group export: A user-requested, shareable snapshot of one configuration group and all of its descendant groups and server configurations. It carries the credentials and configured plugin options needed to recreate those servers, and may include servers that are not activation candidates. It is available only when the selected subtree contains at least one server configuration.
- Server form draft: The UI-held draft of one server configuration's connection fields, shared by the create and edit form faces. It loads from the edit command's resolved form state and commits as one
ServerEditDraftcommand; field state, password reveal, and the form grid live in one shared module, so a new form face reuses the same load/commit pair. - Manual group: A configuration group owned by the user. It may contain only manually owned nodes; deleting it recursively deletes its descendants. Deletion is always user-confirmed: one confirmation for an empty manual group, and a second confirmation naming the recursive subtree scale when it is non-empty.
- Subscription: A remote configuration source with a persistent opaque UUID and a fixed subscription-group identity. Its URL is a mutable endpoint/credential reference; editing the URL keeps the source identity, while deleting and creating a subscription establishes a new source identity.
- Subscription group: The fixed, source-owned configuration subtree provided by one subscription. Users cannot move or structurally edit its nodes. A subscription server's user-visible name is one remote-derived value from
remarks, falling back to its address and port when absent, empty, or whitespace-only; each refresh derives that name from the latest remote record. It has no local alias or note override. - Active target: The persisted server configuration or configuration group selected for proxying. When a group is active, the group UUID remains the target even when
sslocalchooses an individual descendant server. - Activation candidate: A server configuration with no known local blocking problem for proxy configuration generation. This status does not guarantee remote reachability or correctness of opaque plugin options.
- Managed plugin: A SIP003 plugin executable that the product packages, hash-verifies, re-signs, and distributes only inside the app bundle. The managed set changes only with app releases; users cannot supply their own plugin executables.
- Proxy mode: The user-facing way traffic is routed through the local proxy: rule mode, global (public targets default through Shadowsocks, fixed local targets stay direct via a minimal ACL), or direct mode through an ACL-backed local SOCKS endpoint. All three project the local SOCKS and HTTP inbounds as the system-proxy target.
- Rule mode: The ACL-backed routing mode in which built-in (and later custom) rules choose proxy versus direct per target; the unmatched default action is a separate persisted sub-option.
- Rule default action: The rule-mode sub-option that decides proxy versus direct for targets no rule matches. Factory default is proxy-when-unmatched. Avoid: fallback (ambiguous).
- Built-in rule snapshot: An app-shipped, pinned rule set produced by an explicit maintainer update through the sole Python conversion path; Swift validates and consumes snapshots rather than duplicating upstream conversion; ordinary builds only read local snapshots and never fetch or convert. Three sources ship today: geolocation-cn (China domain direct candidates), china-ipv4 (China mainland IPv4 CIDR direct candidates from gaoyifan/china-operator-ip), and gfwlist (proxy candidates from the official Base64 AutoProxy GFWList; domain anchors become suffix matches; URL prefixes with an empty or root path, no query, fragment or user information, and a literal domain host become exact matches, discarding scheme and any valid port;
@@exceptions fully covered by a proxy rule are omitted). A snapshot ships only the rules that can decide routing; same-action absorbed entries and opposite-action shadowed exceptions are omitted at conversion time and never re-enter matching when a broader rule is later disabled. Each snapshot carries one source identity in metadata for the whole set, plus digest, license, attribution, and conversion loss report. Schema 2 and converter 2.0.0 ship compact JSON rules containing only action and match; old snapshots and rule counts outside the generator bounds fail loading. - Custom rule: A user-owned proxy or direct rule with a stable opaque identity and an exact-domain, domain-suffix, or IPv4/IPv6 address-range match; list order carries no routing priority. Same-action absorption and opposite-action shadowing do not erase saved rules or imply that they determine routing; rules violating the fixed local bypass policy are invalid. Avoid: user rule.
- Rule identity: The normalized match condition together with its routing action, shared by equivalent entries from different sources. It is distinct from the persistent identity of an editable custom entry; an individual IP address is equivalent to its full-length CIDR.
- Rule source membership: The collection of sources containing a rule identity; browsing merges equivalent entries while retaining every source membership. Removing a custom entry does not remove matching built-in entries.
- Rule disablement: The user's persisted exclusion of a normalized match condition and routing action across all rule sources; it excludes that rule identity without removing source entries or excluding broader rules covering the same targets. The fixed local bypass policy cannot be disabled.
- Rule absorption: A same-action coverage relationship in the analyzed rule collection in which a broader rule makes a narrower rule redundant for routing; excluding the broader rule makes surviving narrower rules eligible for matching again. Snapshots do not preserve absorbed candidates, so built-in entries omitted at conversion stay omitted even when the broader rule is disabled. It is distinct from opposite-action shadowing and does not erase the user's saved custom rule.
- Rule shadowing: An opposite-action coverage relationship in the analyzed rule collection; it may be complete or partial and may involve the fixed local bypass policy. Built-in shadowed exceptions are omitted from snapshots and are not restored when their blockers are disabled. It is distinct from same-action absorption and does not imply runtime activation.
- Snapshot conversion report: The numeric conversion counts, omissions, rejections, and absorbed total recorded when a built-in rule snapshot was produced. It contains only converted and absorbed totals plus skipped/rejected category counts; shadowed exceptions are counted only as absorbed. It never lists individual upstream entries. It describes snapshot creation rather than the current collection's coverage relationships or runtime state.
- Rule document session: The app-process owner of the final saved custom-rule document. Atomic persistence updates this fact; a runtime application failure never replaces it with an older document. Browsing and runtime projection consume it without rereading the file after a commit. A new app session reads the persisted document again.
- Built-in rule snapshots: The app-process source module shared by browsing and runtime projection. It loads each bundled source on demand, shares concurrent attempts, retains successful snapshots for the process lifetime, and retries a failed source only on an explicit retry request.
- Offline rule test: A rule-only explanation for a supplied domain, IP address, or URL host that returns proxy, direct, or no matching rule, independent of proxy mode, runtime state, and rule default action. It performs no DNS resolution or connectivity check and explicitly leaves DNS-dependent IP rules untested for domain inputs.
- Proxy mode selector: The common user action for choosing rule, global, or direct mode; it is distinct from enabling or disabling the proxy runtime, from the system proxy switch, and from custom-rule management. Its choice persists across GUI restarts.
- Agent switch: The user's persisted on/off intent for the background proxy runtime, distinct from whether that runtime is currently healthy. Its default is off. Avoid: proxy switch (ambiguous with the system proxy switch).
- System proxy switch: The user's persisted intent to use 2.0's local proxy endpoints through macOS system proxy settings, distinct from whether those settings are currently applied or suspended. Its default is off. Avoid: proxy switch (ambiguous with the agent switch).
- System proxy configuration: The device-wide proxy settings associated with macOS network services, shared across applications and accounts. Avoid: global proxy configuration (ambiguous with global proxy mode).
- Network service: A named macOS network configuration entry with its own proxy settings, including entries that are not currently connected. The Home warning calls these entries “网络接口” and identifies them by their names in System Settings; they are distinct from physical interfaces and terminal-command address candidates.
- Desired system proxy configuration: The proxy values currently required by 2.0's saved settings, independent of the values actually present on each network service.
- Typed system proxy configuration: The complete set of desired SOCKS, HTTP, HTTPS, PAC, auto-discovery, simple-hostname exclusion, and exception values managed by 2.0. Avoid: complete proxy dictionary (can include fields outside 2.0's managed values).
- System proxy configuration difference: A current semantic mismatch between a network service's managed proxy values and the desired system proxy configuration. It is neither a historical change log nor proof of which application changed the settings.
- System proxy repair: The user's explicit request to align currently differing network services with the desired system proxy configuration. Avoid: restore (suggests restoring another application's prior values).
- System proxy suspension: The temporary condition in which system proxy settings have been cleared because the background runtime or the current mode's exit is unavailable, while system proxy intent remains enabled. Failed clearing is a cleanup failure, not successful suspension.
- Additional system-proxy exception: A user-added hostname, domain, IP address, or CIDR that macOS keeps outside 2.0's system proxy. The list defaults to empty and does not change the app's fixed local bypass policy or ACL rules. Avoid: bypass list (blurs user additions with app-fixed policy).
- Fixed local bypass policy: App-owned local IP ranges and hostnames that stay direct whether traffic is handled by macOS system-proxy settings or reaches a local proxy inbound. Users can inspect this policy but cannot edit it through additional system-proxy exceptions.
- Login item: The GUI app's registration in the user's login items. It is an independent preference domain: off until the user enables it, and its state is only ever the system login-item registration status.
- Independently saved setting item: A user-facing setting or coherent group of related values that the user explicitly saves as one unit; saving persists and applies that item without committing unrelated unsaved settings. Avoid: immediate edit (can imply applying on every keystroke).
- HTTP proxy inbound: The permanent local inbound that accepts HTTP/HTTPS proxy requests through the external tunnel service and its active ACL; it is not a user preference and cannot be disabled, and it is distinct from proxy modes and from the Legacy Privoxy adapter. Avoid: HTTP proxy mode.
- SOCKS5 UDP relay: The fixed capability of the local SOCKS5 endpoint to accept UDP ASSOCIATE traffic and relay it through the proxy runtime. It is not a user preference and does not imply that all system UDP traffic is proxied. Avoid: system-wide UDP proxy, automatic UDP proxying.
- Legacy Privoxy adapter: The frozen implementation's HTTP(S)-to-local-SOCKS5 bridge. It is a Legacy-only dependency and is not migrated into 2.0.
- Listener mode: The user-facing choice that determines which address families the local proxy listeners accept: this Mac only (IPv4 loopback), all IPv4 interfaces, all IPv4 and IPv6 interfaces, or all IPv6 interfaces only. One mode applies to both the local SOCKS and HTTP proxy inbounds. Avoid: Listen scope.
- HTTP proxy environment command: An HTTP-only terminal command that sets lowercase
http_proxyandhttps_proxyfor this Mac's local HTTP proxy inbound. It always targets a loopback address for the selected listener mode. - Terminal proxy environment commands: Shell-specific commands for zsh/bash and fish that set lowercase
http_proxyandhttps_proxythrough the local HTTP inbound,all_proxythrough the local SOCKS inbound, andno_proxyforlocalhost,127.0.0.1,::1, and.localhostnames. The proxy endpoints use the terminal command address (defaulting to a compatible loopback address), and the commands affect child processes that honor these variables. - Terminal command address: The user-selected IP address used by both HTTP and SOCKS endpoints in copied terminal proxy environment commands. It defaults to a compatible loopback address; non-local listener modes also offer addresses of enabled Wi-Fi and Ethernet interfaces in the accepted address families, excluding rotating temporary addresses, and the selection lasts only for the current app session without changing listener or system-proxy settings.
- Proxy runtime: The per-user background host process, independent of the GUI's lifetime, that runs the external tunnel service and loads the optional generated ACL sidecar.
- Legacy configuration: The server list and preferences persisted by the frozen implementation in
Legacy/. - Legacy import: The user-confirmed, read-only translation of a Legacy configuration's server records into a separate manual group in 2.0. It migrates no preferences, activates no target, and neither starts proxying nor alters the Legacy source.
- Runtime configuration file: The derived JSON document used by the external tunnel service for the active target. It carries only connection-effective values (endpoints, credentials, encryption, plugin, listeners, ACL); user-visible display names are catalog metadata and never enter the file. Fields NG2 omits are left to the bundled
sslocaldefaults; the file is not the user-managed server configuration or subscription document. - Prepared runtime contract: A deployment-local value holding one runtime document, its encoded JSON bytes, and the digest of those bytes. Planning, persistence, and that deployment's health confirmation share the value; it is not a cache of disk state.
- Runtime convergence: The GUI-side operation that aligns the deployed runtime with one derived runtime definition: it persists the supplied intent, re-expands the committed catalog, derives the runtime document, plans and executes deployment actions under a convergence ticket, verifies launch health, and restores the previous definition as a whole on failure. A change whose derived contract bytes are unchanged against a healthy running runtime performs no convergence work: no contract write, no reload signal, no restart, no launch health re-probe, and no system-proxy reconvergence.
- Convergence ticket: The snapshot of the three concurrency generations (flow, mode change, runtime preparation) that one Runtime convergence captures at entry and rechecks after every await, together with the live agent-switch intent. The four invalidating facts are checked as one; no convergence path checks a subset. The ticket advances its flow component to the occupied generation of the execution it performed and its preparation component after a derivation it triggered. A superseded convergence may not change runtime state, persist intent, or write contracts.
- Rules intent ticket: The fact snapshot that one rule-page operation captures at entry and rechecks as one after every await: the loaded collection with its version, and for write intents additionally the admission gates (complete page, no in-flight transaction, commit seam present, user document loaded). An incomplete page or an advanced collection version supersedes the intent, which then exits without publishing or committing. Address tests capture the whole address-test state so any invalidation drops late results.
- Sensitive information: Server passwords, plugin options (sensitive in their entirety), and nonempty user-provided remote URLs, including subscription URLs.
- Credential exposure boundary: This product reduces accidental disclosure and exposure to other user accounts, but does not guarantee protection against a compromised same-user process, root access, or APFS snapshots and backups; it does not promise secure erasure.
- Credential reference: A non-secret association from a server configuration, subscription, or remote-URL setting to its durable credential; the credential value is kept outside the configuration tree and resolved only when needed.
- Diagnostic report: A user-requested, redacted document containing only approved runtime state, endpoint metadata, aggregate catalog facts, managed-plugin presence, file metadata, and safe GUI event lines. It never contains passwords, plugin options, Keychain values, full remote URLs or tokens, runtime JSON, server addresses, remarks, or raw wrapper log text.
- Diagnostic log view: The explicit view/copy surface for GUI event lines and the raw wrapper
agent.logtail; child-process verbosity followssslocaldefaults unless an externalRUST_LOGis inherited. Raw wrapper log text is not part of a diagnostic report. - Diagnostic facts: Aggregate, redacted facts about the catalog and proxy runtime that can enter a diagnostic projection without exposing catalog entries, server addresses, remarks, credential references, URLs, paths, or other sensitive information.
- System-level shortcut: A user-configurable keyboard action registered by the app that can trigger an app action even when the app has no focus. Avoid: global hotkey, global shortcut.
- Window-local default action shortcut: A keyboard action bound to the focused app window or form context, such as confirming a dialog; it is distinct from a system-level shortcut.
- Workspace destination: A top-level location in the single 2.0 GUI workspace, such as home, servers, subscriptions, proxy rules, settings, or diagnostics. It is navigation state only; it does not contain a node selection, sheet, alert, or feature draft.
- Workspace route: The in-process navigation choice and intent used to move between workspace destinations from the workspace or another app entry point. It does not persist user configuration or own feature-local state.
- Workspace window: The single main window hosting the workspace. It is presented at every launch unless silent launch is on; while it is open the app presents in its regular form, and once it is closed the status menu is the only way to reopen it.
- Adaptive app form: The app's presentation, which follows its workspace and independent rule-report windows: a regular app with a Dock icon and app-switcher presence while either is open, and a menu-bar-only accessory app with the status menu always available after the last window closes. Avoid: menu bar form (the presentation is no longer constant).
- Silent launch: The user preference that a launch present no workspace window, starting in the menu-bar baseline instead. It defaults to off, takes effect on the next launch, and changes nothing about proxy runtime restoration or the login item.
Relationships and invariants
- Top-level catalog children may be server configurations or groups; there is no artificial user-visible “uncategorized” group.
- Server and group identities are opaque, persistent UUIDs independent of display name, position, or parent; IDs are not reused for another logical node.
- Each group stores an explicit child order. A node cannot be shared by multiple groups, and group edges must remain acyclic.
- Manual and subscription ownership form separate subtrees; a manual group cannot adopt subscription-owned nodes, and subscription-owned nodes cannot be moved into manual groups.
- New manual servers always receive a new UUID rather than being deduplicated by endpoint or serialized content; Legacy migration preserves an available legacy UUID.
- Creating or editing a manual server requires a name that is nonempty after trimming leading and trailing whitespace and newlines; rejection changes neither the catalog nor credentials. URI, subscription, and Legacy imports preserve nonblank supplied names and generate a name from the address and port when the supplied name is absent, empty, or whitespace-only:
address:portfor a hostname or IPv4 address and[address]:portfor an IPv6 address. A generated manual-server name is an ordinary saved name; later endpoint edits do not update it automatically. Names remain catalog metadata rather than activation eligibility or runtime connection fields. - Server configurations and configuration groups have no separate node-level enable/disable state. A server configuration is an activation candidate only when no known local blocking problem prevents proxy configuration generation.
- A server configuration persists its SIP003 plugin program reference and plugin options verbatim. Editing offers only managed plugins as selections; a non-empty but unsupported encryption method or a reference to a program outside the managed set remains stored but makes the server an invalid activation candidate. Opaque plugin options are retained without being declared valid or invalid by the app.
- A user-initiated edit to a server configuration that changes credential-bearing fields commits the configuration and its credential values as one logical change; if persistence fails, the previous configuration and credential values remain in force.
- Plugin programs are distributed only as managed plugins; their availability changes only with app releases. If an active target's plugin becomes unavailable, the target is cleared and proxying stops without fallback.
- Activating a server selects that one server. Activating a group recursively expands activation-candidate server leaves in explicit child order and passes them to the external proxy service; invalid leaves are omitted and surfaced, while a group with no activation candidate rejects activation atomically.
- An activation command outcome is self-contained: success carries the count of skipped invalid leaves and an atomic rejection carries its typed
ActivationFailurereason. One session-scoped activation feedback model owns pending state and last-command feedback for the three activation entry points (home target tree, sidebar context menu, group detail) and is single-flight across them; unexpected errors are recorded as feedback and rethrown so each surface chooses inline or alert presentation. Runtime-published activation facts remain the channel for global surfaces (status menu, status card, diagnostics). - The active target remains the selected node ID and is not silently replaced by a descendant chosen by
sslocal. If it is deleted, has no activation candidate, or is otherwise invalid, the target is cleared and proxying stops rather than falling back silently. - After a committed edit to the active target's subtree, the target is re-expanded immediately: a non-empty set of activation candidates updates the runtime atomically; no activation candidate clears the target and stops proxying without fallback.
- A committed catalog edit that leaves the runtime configuration file's bytes unchanged — including edits outside the active target's subtree, remark-only changes, and no-op re-saves — triggers no runtime convergence work: no contract write, no reload signal, no restart of the external tunnel service, and no launch health re-probe. Catalog revalidation (activation-candidate skips, target invalidation) still happens on every commit. Within an active group subtree, a connection-value change — including member order — does change the runtime file and converges.
- Manual nodes may move between the catalog root and manual groups, carrying their subtree and child order without changing identity or ownership; cross-source moves, shared parents, and cycles are rejected.
- Manual catalog ownership is independent of proxy mode: removing the manual proxy mode does not remove manual groups, manual servers, or records imported into a manual group.
- Empty groups may persist and remain editable, but cannot be activated. Deleting a server configuration or an empty manual group takes one user confirmation. Deleting a non-empty manual group recursively deletes its descendants and takes a second confirmation that names the subtree scale and that the subtree's server credentials are removed.
- A subscription owns its subscription group and its refreshed members; manual groups remain user-owned. Remote connection fields, membership, structure, order, and
remarksare authoritative; subscription nodes have no local eligibility overlay. - Subscription server and nested-group identities use provider-supplied stable IDs scoped to that subscription. When a remote server record has no stable ID, continuity depends on an exact match of its server address and port; changing the name, password, encryption method, plugin program, or plugin options preserves identity. Records without stable IDs that have the same address and port are duplicate identities and reject the whole refresh, even when other fields differ. Distinct provider IDs allow servers at the same endpoint to coexist. Paths and partial endpoint matches must not merge identities.
- Subscription-owned nodes and nested groups permit no local connection-field, name/remark, membership, parent, order, or eligibility override.
- Each subscription refresh is a complete, structurally valid snapshot committed atomically. A valid empty snapshot replaces the remote subtree with an empty group; transport, HTTP/authentication, decoding, schema, duplicate-ID, or required-field validation failures leave the last successful snapshot, active target, and runtime state unchanged while marking the source stale. A structurally complete record with an unsupported method or plugin remains in the committed snapshot as an invalid activation candidate. An initial failed refresh leaves an empty group with an error state.
- The private
x_shadowsocksx_ngextension is optional decoration on the standard SIP-008 server list. When it is absent, uses an unsupported schema version, or fails validation — unresolvable child references, duplicate or ID-colliding groups, shared parents, or cycles — the refresh still succeeds: the standard flat server list becomes the direct children of the subscription group and only the extension is discarded. - Editing a subscription URL keeps the subscription, fixed group, and remote identity namespace; the last successful snapshot remains available until the new URL succeeds. To isolate a different source, delete the old subscription and create a new one. Deleting a subscription removes its source, fixed group, and remote members after explicit confirmation; an active target is cleared and proxying stops without fallback.
- A Legacy import reads a current snapshot and commits atomically; structurally invalid records are omitted with a report, while unsupported methods or plugins are preserved as invalid activation candidates. Skipping leaves an import entry available, success prevents automatic repetition, and an explicitly requested re-import creates a separate manual group.
- A unique, syntactically valid Legacy server UUID is preserved. An otherwise valid server with a missing, malformed, or duplicate UUID receives a fresh UUID; invalid records are omitted with a user-visible report.
- A Legacy import migrates only server records; mode, target, login-item, and all other preferences stay untouched. Legacy passwords, plugin options, and user-provided remote URLs move to credential references; generated configurations, caches, diagnostics, logs, and binaries do not migrate. The imported plugin program reference is preserved verbatim even when 2.0 does not provide that plugin.
- 2.0 and Legacy are independent applications that may run concurrently. 2.0 never stops Legacy processes, retires Legacy runtime jobs, or transfers preferences; a Legacy import brings over server records only. Their factory-default local endpoint ports are distinct, but enabling 2.0's system proxy switch may replace proxy values in the active network location regardless of which app wrote them.
- The local SOCKS and HTTP proxy inbounds share one listener mode; the factory default is IPv4 loopback. The other modes bind all interfaces of their selected address families and expose unauthenticated proxy endpoints, whose reachability from other devices depends on the firewall and network. Terminal proxy environment commands are for this Mac's shell and default to a compatible loopback address even when listeners bind to all interfaces; in non-local modes the home command card can point them at an enabled Wi-Fi or Ethernet interface address (see Terminal command address), without changing listener or system-proxy settings.
- Saving an independently saved setting item persists and applies only that item's complete value. Other unsaved settings remain pending and cannot overwrite the saved item when they are later committed.
- Listener mode is an independently saved setting item. Saving it does not commit unrelated settings or start a disabled proxy runtime; if the runtime is running, the listener change is applied by restarting it. A saved mode remains selected if runtime convergence fails, and the proxy status UI owns runtime status and restart-failure presentation rather than the settings view or editor.
- The ports of the locally provided proxy endpoints are explicit user configuration in the range 1000–65535: the system never rewrites a port on its own, and a port conflict rejects proxy start with an error naming the endpoint until the user explicitly chooses a new port. A recovery suggestion only proposes candidate ports (32768–65535, free and distinct from the other endpoints' configured values) and applies only after the user confirms and saves.
- The SOCKS5 and HTTP listener ports are one independently saved setting item and are validated and persisted together. Invalid or duplicate values and known port conflicts block saving, except when the complete proposed listener configuration matches the active runtime; unknown occupancy is a warning that does not block saving, and a suggested port remains pending until the user saves.
- A successfully saved port item remains persisted if runtime convergence fails; the UI reflects the saved port values and does not need a separate runtime-error message for this item.
- The external tunnel service starts and stops with the proxy runtime: its SOCKS and HTTP inbounds are reachable exactly while the agent is running, and neither depends on the GUI being open. The agent runs when the agent switch says so: off by default, and — once turned on — even without an active target it keeps listening with an empty server list.
- Diagnostic collection is read-only: it never changes the configuration catalog, credentials, proxy runtime, or system proxy settings. A diagnostic report is best-effort; an unavailable fact source is represented as unavailable rather than causing unrelated available facts to be discarded.
- A diagnostic report contains aggregate catalog facts only. The complete configuration catalog, catalog entries, server addresses, remarks, and credential references never cross into the diagnostic report projection.
- Raw wrapper log text may be viewed or copied only through the explicit diagnostic log view and never enters a diagnostic report or safe diagnostic projection.
- Exported GUI event lines are an allowlisted, field-cleaned subset of runtime events; raw event detail remains available only through the explicit diagnostic log view.
- A diagnostic report presents fixed proxy-state categories rather than free-form failure detail. If its safe projection cannot be constructed, no report is emitted; an unavailable individual fact source remains a marked partial fact instead.
- A diagnostic export is considered complete only after the user-selected report file is successfully written; opening the export panel or preparing report text is not an export completion.
- Proxy modes are mutually exclusive: rule mode with proxy-when-unmatched writes the local SOCKS and HTTP/HTTPS system proxies and runs sslocal with a
[proxy_all]ACL whose bypass list is the fixed local safety rules plus China domain and China IPv4 CIDR direct candidates from the built-in snapshots; rule mode with direct-when-unmatched runs sslocal with a[bypass_all]ACL whose proxy list holds only expressible, unshadowed GFWList proxy candidates (the two default actions never unconditionally union all built-in sources), so unmatched targets and IP literals with no matching rule go direct; global writes the local SOCKS and HTTP/HTTPS system proxies and runs sslocal with a[proxy_all]ACL whose bypass list is only the fixed local safety rules (no China list, GFWList, or custom rules); direct mode writes the local SOCKS and HTTP/HTTPS system proxies and runs sslocal with[bypass_all], so SOCKS and HTTP requests go direct without a Shadowsocks server. IP-CIDR matching may cause sslocal to issue local DNS queries for unmatched hostnames; the product does not promise that all DNS queries travel through the remote Shadowsocks server. In every mode, the system proxy ExceptionsList contains fixed local IP ranges,localhostand.localhosts, plus the user's additional system-proxy exceptions (empty by default); the GUI explicitly supplies the simple-hostname exclusion value, defaulting it to on. Applying a typed system proxy configuration disables PAC and auto-discovery, removes PAC URL and JavaScript values, and preservesProxieskeys outside its explicit field set. User-added exceptions affect only macOS system-proxy traffic and never enter the ACL. The ACL keeps its fixed local IP and host rules, including the no-dot hostname rule, as the direct-routing guarantee for requests that reach sslocal, including LAN-sharing clients. CGNAT is excluded. IPv6 CIDR strings in system exceptions are not a verified bypass. GFWList@@exceptions shadowed by a broader proxy rule are omitted from the ACL and recorded in the conversion report; the product does not claim complete reproduction of GFWList URL/protocol conditions. A change to the active ACL's summary or digest requires a full sslocal restart; the ACL's runtime path is a stable reference and does not change when the proxy mode switches. The wrapper publishes the ACL-backed receipt only after the child owns every configured TCP listener; the controller retains the previous mode and runtime until this instance is verified and restores the previous runtime on verification failure. When a rule/global active target becomes invalid, the agent can keep listening without an available exit; this is distinct from a healthy, usable system proxy. Only traffic that honors the system SOCKS or HTTP/HTTPS proxy settings or explicitly connects to a local inbound is routed; the product does not claim interception of all application or UDP traffic. - Preferences have no single restore-all-factory-defaults action. Where a reset exists, it is scoped to one configuration item, never to the whole preference set at once.
- The proxy mode selector's choice and the rule default action persist with the settings snapshot and survive GUI restarts; a persistence failure keeps the previous mode and sub-option in force and names the reason. Rule, global, and direct mode are always available.
- The GUI is the user-facing manager; the Shadowsocks tunnel service is an external runtime boundary rather than part of the GUI's domain model.
- Runtime file deployment prepares contract bytes once and still reads the actual disk contract before choosing actions. ACL digest-manifest hits avoid reading the variant body; a miss reads the existing variant once and reuses those bytes if rollback is needed. An unchanged active-link target is preserved. The synchronous transaction records only completed mutations and attempts every recorded restoration on failure, reporting any restoration failure explicitly. A decoded contract with no ACL body can restore only an existing regular variant file; a missing or unsafe variant fails before publishing its link or contract. Digest metadata is updated only after contract persistence succeeds.
- The Proxy Rules destination provides browsing through
RulesWorkflow: it prepares a versioned collection off the UI actor from fixed local policy, all three built-in snapshots, and the custom-rule document. Equivalent identities retain all source memberships and custom UUIDs. Search, source, and action filters intersect; browsing preserves stable source input order with no user sorting; hidden selections are cleared and navigation preserves session query/selection. Source metadata and original conversion reports remain distinct from current coverage explanations. Missing/corrupt inputs explicitly mark the collection incomplete while retaining available sources. The page also tests domains, IP literals, and URL hosts offline against that complete saved collection, independently of browsing filters, runtime state, and the unmatched default action. Results retain all deciding identities and source memberships plus other target-specific hits; domain tests explicitly leave IP rules untested without DNS. Invalid input and incomplete collections are operation errors. Editing the target or retrying clears prior results, and intent tickets reject late tasks. The page supports individual and batch enablement through the Workflow. A schema-2 user document atomically stores custom entries and source-independent disabled identities; schema 1 reads with no disabled records. Disabled identities survive source disappearance and are visible under all sources with the disabled filter, where enabling removes orphan rows. Filtering excludes disabled identities before current coverage analysis and offline matching; runtime analysis separately uses the existing default-action source subset and actual ACL skeleton. Commits finish when the validated rule document is atomically saved and return the saved document. Only persistence blocks repeated edits; runtime application and browsing analysis never prolong a rule operation. Saved enablement, source memberships, custom entries, counts, and document version become available immediately. Coverage explanations retain their previous values while background analysis catches up, with no additional waiting or invalidation UI. Browsing analysis and runtime application independently coalesce nearby saves and consume the latest saved document; stale analysis never replaces newer saved facts. An unchanged document retains the collection. There is no ordinary toolbar reload or post-commit file reread. Initial loading shares bundled source facts with runtime preparation; only failure actions explicitly retry failed sources. Success feedback counts actual saved changes and expires after three seconds; validation and persistence failures remain dismissible with details and contextual retry actions. Runtime application failures belong to proxy status, independently of rule-operation success. A failed source-loading preparation retains cached rows and blocks edits until an explicit successful retry. Runtime projection and ACL compilation use immutable captured inputs off the UI actor, reject superseded intent before deployment, and reuse the compiled document for convergence checks and deployment. Rule deployments run serially and only changed ACL identities restart sslocal. A save during deployment queues convergence to the latest document after that deployment settles. Deployment failure attempts to restore the previous runtime without restoring old saved rules; recovery failures remain explicit runtime errors. Failed rule application is not retried in a loop; another saved change or explicit agent-on retry requests convergence again. The window toolbar adds a single custom rule and custom memberships expose per-row editing through a native sheet. Sheet-local versioned drafts support IPv4/IPv6 IP literals and CIDRs, ASCII/Punycode exact domains and suffixes (including the existing national suffix), normalized previews, and enabled-collection coverage explanations. Previews run outside the UI actor and prepare only the prospective row; explicit saves revalidate the saved version and reuse the same document transaction and feedback path as enablement. Duplicate custom identities and fixed-local proxy conflicts block saving; builtin equivalence and explanatory absorption or shadowing do not. Editing preserves the custom UUID and source metadata, moves only the custom membership, inherits disablement onto the new identity, and retains old disabled records. Additions do not clear an existing disabled identity; edits that would inherit disablement onto an immutable fixed identity are rejected. Cancellation and navigation discard the draft; in-flight commits block repeated saves and interactive dismissal. The operation area counts selected custom memberships and offers single or batch deletion. Workflow preparation captures an immutable custom UUID set and collection version for one confirmation; filtering clears hidden selections, and later selection changes cannot expand the captured set. Confirmation rejects stale or incomplete facts through the same feedback surface as commit failures and uses the same whole-document transaction, retaining builtin memberships and all disabled identities, including orphan records. Saved deletion updates source counts and selection immediately, recomputes relationships in the background, and invalidates the address test; unchanged ACLs do not restart runtime. Cancellation only discards the confirmation. - The app presents in the adaptive app form: menu-bar-only after the last workspace or independent rule-report window closes, and with a Dock icon and app-switcher presence while either is open; hiding the app is not closing it and does not change the form; the GUI process keeps running with the status menu available regardless of the workspace window.
- At launch the GUI presents the workspace window unless silent launch is on; after the workspace window is closed, the status menu is the only way to reopen it. Silent launch changes only window presentation: proxy runtime restoration happens at every launch regardless of it.