Commit Graph
39 Commits
Author SHA1 Message Date
Qiu Yuzhou bdcbc6cd60 feat(servers): separate details from edit forms 2026-10-06 12:25:13 +08:00
Qiu Yuzhou a31e2fa4bf docs(adr): 记录源码目录即分层与主题及守卫约定
App/ 按分层、Domain/ 按主题组织,两个根层只放子目录、不放平铺文件;边界由
Tests/SkeletonTests.swift 的守卫正面圈定断言,而不是禁令加豁免登记。

ADR-0029 只记录组织原则本身:分层与主题划分的依据(Domain 主题名取自
GLOSSARY.md 的章节划分)、四条被否决的备选(平铺加豁免登记、feature-first、
Domain 照搬分层、守卫锁定目录集合),以及两条在代码里不可见的 XcodeGen
约束和跨进程契约按文件引用的代价。

ADR 不维护目录与文件清单——清单由 ShadowsocksX-NG2/README.md 的工程结构
一节维护,避免记录随事实漂移。

project.yml 的两处分层注释与 README 的工程结构一节各加一处引用:前者按该
文件既有写法引用编号,后者用完整链接。
2026-10-05 20:27:11 +08:00
Qiu Yuzhou 0f2855f453 fix(ui): 关闭服务器表单密码框的系统密码自动填充
系统对 NSSecureTextField 默认提供钥匙串/密码 App 的自动填充入口,且不
取决于字段语义:实测底层字段 contentType 已是 nil,既有
.textContentType(nil) 不足以关掉它,AppKit 也没有公开开关。

改用字段级私有开关 _setPasswordAutofillEnabled:(responds(to:) 守卫,
方法消失时静默回退、不崩溃)。PasswordAutofillOptOut 以同帧背景探针
认领承载密码控件的文本框,只关密码列自己;明文/密文切换换成新实例后
重新认领。

新增 PasswordAutofillOptOutTests 固定认领范围与切换行为;全量单测
1022 通过,人工验证通过。取舍与后果记入 ADR-0028。
2026-10-05 19:23:55 +08:00
Qiu Yuzhou 5968b1fd0b feat(import): 统一服务器导入入口并支持 SIP-008 文件导入
新增统一导入面板(拖放多文件/剪贴板 ss:// 一键/旧版直接导入),来源按内容
嗅探分派(位图→二维码、JSON→SIP-008、其余→文本行),逐来源独立事务与
结果。深模块集中在 CatalogWorkflow.importServers 单一接缝,凭据统一经
journal,退役 ss:// 文本框、二维码预览、Legacy 专属 sheet 与首启主动弹窗。
SIP-008 导入语义见 ADR-0027(文档 ID 只作装配键,目录身份全新生成)。
2026-10-04 21:45:20 +08:00
Qiu Yuzhou 2f670a4c6b refactor(rules): decouple saved intent from runtime application 2026-10-03 17:40:05 +08:00
Qiu Yuzhou 3aae39799a fix(rules): convert root URL paths to exact domains 2026-10-03 09:56:00 +08:00
Qiu Yuzhou 6e69d92449 feat(rules): convert pathless URL prefixes to exact domains 2026-10-03 09:52:42 +08:00
Qiu Yuzhou a5d96dc990 refactor(rules): keep one conversion path 2026-10-03 02:55:48 +08:00
Qiu Yuzhou fcd5645b68 refactor(rules): slim built-in snapshots 2026-10-03 02:34:04 +08:00
Qiu Yuzhou 59ba9dfeff feat(rules): enhance rule identity documentation and introduce offline testing explanation 2026-10-02 22:38:11 +08:00
Qiu Yuzhou cb5a535297 feat(rules): simplify details and open conversion reports in a window 2026-10-02 20:38:23 +08:00
Qiu Yuzhou 640ebf9f51 docs: record system proxy repair and suspension design (#73) 2026-10-02 10:28:22 +08:00
Qiu Yuzhou 5c1781c2b8 docs: revise terminal command loopback convention for #72
按 issue #72 设计同步修订:ADR-0015 命令地址段落改为「默认兼容回环 +
非本机方式会话内可选 Wi‑Fi/Ethernet 地址」,表格列名改默认命令地址;
CONTEXT.md 命令定义与监听约束同步;设计文档标记已实现。健康检查与系统
代理目标仍由监听方式确定。
2026-10-01 10:07:05 +08:00
Qiu Yuzhou ab5a153477 docs(adr): record hostless unit tests and the appBundle resource seam in ADR-0021 2026-09-30 20:45:04 +08:00
Qiu Yuzhou ba20e42f11 docs(adr): record BundleProgram key and throttled re-register in ADR-0020 2026-09-30 19:15:33 +08:00
Qiu Yuzhou d2a8d6105c feat(system-proxy): route writes through privileged LaunchDaemon helper
Implements issue #71 with ADR-0020 (supersedes ADR-0019's endpoint-signature
cleanup, startup cleanup, and health-gate cleanup).

- New ShadowsocksX-NG2SystemProxyHelper target: app-bundled root LaunchDaemon
  (SMAppService.daemon, on-demand via MachServices) that mechanically applies
  a closed typed configuration or executes an unconditional clear. Clients are
  accepted by code-signing bundle identifier only; requests run serially on a
  shared queue (last-writer-wins).
- SystemProxyConfiguration is now the closed typed apply payload (Codable over
  JSON XPC): explicit SOCKS/HTTP/HTTPS enable + endpoints, PAC and
  auto-discovery disabled with URL/JS removed, explicit simple-hostname
  exclusion, complete ExceptionsList. Unmodeled Proxies keys are preserved and
  equivalent values skip the commit.
- Cleanup is a side effect of a persisted system-proxy intent on-to-off
  transition only: the switch itself, or the agent-off cascade (persist both
  intents off, clear, then stop listeners; failed cascade persistence records
  no transition and clears nothing). Launch with the switch off, health-gate
  closure, invalid targets, and passive events never clear. Failed clears are
  surfaced without deferred retry.
- Endpoint-signature store, ownership migration, and direct Authorization
  Services writes are removed; old signature files stay inert. Helper
  unavailability or pending approval keeps an enabled intent pending and
  surfaces the Login Items approval path (HomeView button + menu label).
2026-09-30 12:26:40 +08:00
Qiu Yuzhou 08682724c9 fix(system-proxy): reconcile configuration across network changes 2026-09-29 23:39:25 +08:00
Qiu Yuzhou 9284b1c9a1 feat: export configuration groups as SIP-008 JSON 2026-09-29 20:16:05 +08:00
Qiu Yuzhou c50470dc89 refactor(settings): move silent-launch persistence to UserDefaults
Migrate SilentLaunchStore from its versioned JSON file to the app's
standard defaults domain (key `silentLaunch`), the platform's canonical
home for GUI-scalar preferences, so the GUI preference category stops
growing one file per item. Reads stay safe-side strict: a missing key
or a value not bridgeable to Bool loads as not silent (plain
bool(forKey:) would coerce "yes" to true). The file-form
persistence-failure naming path is dropped — UserDefaults.set reports
no failure — so SilentLaunchController keeps its persist-then-flip seam
while errorMessage and its SettingsView presentation go away.

The legacy file never shipped in any release; migrateLegacyFileIfPresent
runs in the production composition root only, preserves the value on
developer machines, and deletes the file (no-op when absent). The test
composition uses a dedicated suite cleared at assembly; unit tests use
per-test suites with removePersistentDomain teardown, never the
standard domain.

ADR-0017 carries a dated amendment (Status plus the affected
consequence line); the settings.json separation rationale stands
unchanged.
2026-09-29 16:06:52 +08:00
Qiu Yuzhou 8b698a0bf1 feat(app): adaptive app form with window-following activation policy
- switch to regular while the workspace window is open (Dock icon,
  app switcher, default menu bar) and back to accessory when it closes;
  driven by NSWindow didBecomeKey/willClose with an anchor-view identity
  filter (scenePhase follows the app rather than windows on macOS, so it
  fires no event on close)
- add silent launch preference (settings-general, off by default,
  persisted in its own silent-launch.json since settings.json is rewritten
  whole from the controller's in-memory snapshot): suppressed launches
  keep the menu-bar baseline and proxy runtime restoration is unaffected
- docs: CONTEXT.md adaptive app form / silent launch terms and invariants;
  ADR 0017 superseding 0016 (scene-based model and reopening carry over)
2026-09-29 15:13:30 +08:00
Qiu Yuzhou 0d0e74d59c feat(ui): menu-bar-only form with scene-based main window
Replace the hybrid form (LSUIElement + runtime activation-policy
switching + AppKit-owned NSWindow) with a plain SwiftUI Window scene:
the app stays accessory at all times, launch presents the workspace
window via defaultLaunchBehavior(.presented), and the status menu
reopens it after close via openWindow. Drop AppDelegate and the
window-opening adapter and intents; WorkspaceRoute keeps navigation
state only, with the scene id defined there. Documented in ADR 0016
and CONTEXT.md.
2026-09-28 19:18:27 +08:00
Qiu Yuzhou cf1834a775 feat(ng2): add four-way listener mode 2026-09-27 20:58:54 +08:00
Qiu Yuzhou 5a05b8a661 refactor(ng2): use sslocal runtime defaults 2026-09-27 17:51:34 +08:00
Qiu Yuzhou e62ce007d7 feat(settings): save proxy ports independently 2026-09-27 17:07:47 +08:00
Qiu Yuzhou 88af8bfdf2 feat(system-proxy): write HTTP/HTTPS alongside SOCKS
Apps honoring only the HTTP proxy family were never proxied under
the SOCKS-only projection. Point system HTTP and HTTPS at the
always-on sslocal HTTP inbound (CONNECT); reject documents lacking
that inbound instead of aiming at port 0. See ADR 0012.
2026-09-27 15:49:03 +08:00
Qiu Yuzhou effae048b8 feat(settings): remove holistic preferences reset
Not every preference warrants a reset; future resets are per
configuration item, added only where a field truly needs one (tracked
in #69). The whole chain goes: the 恢复默认 button and its confirmation
alert in SettingsView, the SettingsWorkflow reset/confirm/cancel
commands and the confirmation machinery they existed for (the
SettingsConfirmation enum had a single case), the
SettingsCommitting.resetPreferences seam entry, the runtime
resetPreferences() and ProxySettingsFileStore.reset() with its
file-rollback helpers and the rollbackFailed store error.

CONTEXT.md drops the three stale reset phrases and now records the
domain decision: no restore-all-factory-defaults action for
preferences; a reset, where it exists, is scoped to one configuration
item. ADR-0004/0005/0011 carry amendment notes since their reset
sentences describe a removed capability (0004's also predate the
PAC removal).
2026-09-27 13:29:32 +08:00
Qiu Yuzhou 8aedf8c42e perf(acl): per-variant ACL files behind a stable active link
Stop rewriting the whole ACL on every proxy-mode switch: materialize one
file per routing shape (acl-<summary>.ini), keep acl-active.ini as a stable
symlink the contract points at, and skip unchanged writes via a digest
manifest. The contract carries only path/summary/sha256 — the agent checks
the link stays inside the runtime directory and never reads ACL bytes.

Agent switch now defaults to off (no server config on a fresh install) and
survives preferences reset, which also returns any held system proxy and
converges a still-running agent onto the factory runtime. See ADR-0011.
2026-09-27 03:14:38 +08:00
Qiu Yuzhou a8cd6262c2 feat(proxy): remove PAC and accept ACL system proxy path
Remove the PAC product feature entirely: PAC HTTP service, port, health
probe, runtime extension, PAC URL system proxy projection, PAC rule
editing, and GFWList URL settings. The product now exposes rule / global /
direct modes only, all projected as a local SOCKS system-proxy target.

First-run defaults are rule + proxyWhenUnmatched, agent on, system proxy
off. Homepage and status menu share one ProxyControlSnapshot showing the
three modes and rule sub-options. Manual servers, subscriptions, Legacy
import, and independent SOCKS/HTTP inbounds are retained.

Fix: include verbose in SslocalListenFingerprint so a verbose-only
contract change restarts sslocal instead of leaving RUST_LOG stale.

Revise CONTEXT.md vocabulary, ADR-0007, and ADR-0009. Record GFWList
exception shadowing, IPv6 system-exception, DNS, and traffic-coverage
boundaries. Add host acceptance evidence document.

Closes #67
2026-09-26 21:58:12 +08:00
Qiu Yuzhou f2e9cff69d feat(proxy): add ACL-backed global mode with local bypass
Global mode now deploys a minimal proxy_all ACL carrying only the fixed
local safety rules, so public targets default through Shadowsocks while
loopback/RFC1918/link-local/ULA/localhost/*.local/no-dot hosts stay
direct for SOCKS, HTTP, and LAN-sharing clients. System proxy exceptions
include that fixed scope; IPv6 system-exception limits are documented
with the ACL as the routing guarantee. Mode switches keep the
full-restart and rollback path.

Closes #62
2026-09-26 12:33:35 +08:00
Qiu Yuzhou 39e25477bb feat(proxy): add ACL-backed direct mode 2026-09-26 11:46:45 +08:00
Qiu Yuzhou ab6acf8912 refactor(ng2): remove UDP relay preference 2026-09-24 22:00:58 +08:00
Qiu Yuzhou 9fc6446237 refactor(ng2): remove manual and external PAC 2026-09-23 18:47:15 +08:00
Qiu Yuzhou fd9be86149 refactor(ng2): add diagnostics workflow module as single UI-facing seam
诊断区收敛为 DiagnosticsWorkflow 深 module(Candidate 03,issue #43):事实
采样、轮询代际、raw 日志投影、报告准备与导出完成登记集中一个 seam,视图只
负责生命周期触发、呈现、剪贴板、保存面板与报告文件写入。侧栏与详情共享同
一份安全摘要;收集只读,不触碰目录、凭据、runtime 与系统代理设置。

目录不再把原始 ConfigurationCatalog 写入诊断快照:CatalogWorkflow 只输出
domain-neutral 聚合事实(计数 + 已知无效数),报告 builder 只接受已裁剪的
report facts。事件导出改为显式白名单派生(diagnosticReportLine):自由
detail、主机公布地址与 runtime 错误原文一律清洗,数量/端口/领域点名原因
保留;新增事件漏 case 无法编译。raw GUI 事件行与 wrapper agent.log 尾部仅
经显式查看/复制提供,永不进入报告(ADR-0006)。导出完成事件只在用户选择的
文件实际写入成功后登记。

RuntimeEventStore 事件改为携带封闭枚举原值,渲染发生在呈现边缘(白名单清
洗只可能作用于原值)。测试:新增 workflow interface 覆盖投毒导出、白名单、
raw/报告分离、缺失标注、只读、代际覆盖与取消;ADR-0006 与 CONTEXT.md 术语
同步入库。
2026-09-22 19:22:31 +08:00
Qiu Yuzhou e242bbf426 feat(ng2): persist proxy mode selection and drop enabled-modes
模式选择(菜单唯一入口)随设置快照持久化,GUI 重启恢复所选模式;持久化
失败保留旧模式并点名呈现,不静默切换。外部 PAC 模式下保存设置时,模式从
新快照解析后与 URL 一起作为一个逻辑变更提交(清空 URL 回落 PAC 并同步
持久化),校验失败先于写入。重置偏好把模式回 PAC。

enabledModes 自 ADR-0002 停止偏好迁移后无任何写入方,且 2.0 无编辑 UI,
按死状态删除:内置三模式恒可选,外部 PAC 仍以已配置的有效 URL 为门槛
(ADR-0004)。

测试:模式持久化与重启恢复、持久化失败保留旧值、外部 PAC 一个逻辑变更
回落;makeController 的 proxyMode 参数改为可选以便测试走恢复路径。
2026-09-22 17:36:24 +08:00
Qiu Yuzhou 376b648ac4 refactor(login): independent opt-in login item
Drop the UserDefaults intent mirror and read the toggle state only from the
system login-item registration status (approval-pending counts as enabled);
default to off until the user enables it, and stop resetting it with proxy
preferences (ADR-0005).
2026-09-22 17:32:46 +08:00
Qiu Yuzhou a7b1b1629a refactor(settings): remove proxy mode selector
Keep mode switching in the status menu as the common control.
2026-09-22 16:31:14 +08:00
Qiu Yuzhou 3dc6043dc6 refactor(ng2): remove system-level shortcuts
Keep proxy controls explicit and leave existing shortcut preferences untouched.
2026-09-22 16:16:58 +08:00
Qiu Yuzhou 46ebe1d835 feat(ng2): simplify Legacy import and isolate default ports (#42) 2026-09-22 14:26:58 +08:00
Qiu Yuzhou ea801d5f38 feat(catalog): remove node enable state
Migrate legacy enabled fields to v3 without preserving their semantics.
Keep recoverable invalid imports visible and filter them during activation.
2026-09-22 08:40:13 +08:00