Commit Graph
67 Commits
Author SHA1 Message Date
Qiu Yuzhou f9e95474e5 refactor: adopt age configuration storage
Replace the custom encrypted container with AgeSwift 0.1.0 so standard
age tools can inspect sensitive configuration independently of the app.

Use independent Keychain X25519 identities for catalog.age and
proxy-listening.age. Preserve atomic commits and confirmed recovery;
reject historical NG2 formats without migration or cleanup.
2026-10-21 14:31:32 +08:00
Qiu Yuzhou 2c75ee67ec feat(runtime): hot reload ACL rules with signals 2026-10-20 23:54:05 +08:00
Qiu Yuzhou 8508080480 fix: observe agent deployment failures without port probes 2026-10-20 23:49:00 +08:00
Qiu Yuzhou 6f33260b3b chore(sslocal): pin fork v1.26.0 2026-10-20 17:21:44 +08:00
Qiu Yuzhou f2424af305 fix(logging): preserve line boundaries and chronology 2026-10-20 16:28:11 +08:00
Qiu Yuzhou 94ef128db8 feat(system-proxy): emit privileged helper events to unified log
The root system-proxy helper logged nothing and launchd discards its
stdout/stderr, leaving helper launches, connection refusals, GUI-side
timeouts and root-side failures invisible. Share the minimal unified-log
emission unit (SystemLog) across GUI, wrapper and helper, and add a
dedicated closed helper event enum (lifecycle minimum: listener ready,
connection accepted/rejected with peer pid, typed apply/clear results)
with ADR-0045 privacy splitting. The emission seam is injected at the
helper engine and listener for test doubles. Helper events are
Console-only: no stderr, no agent.log, no diagnostic view or report
surface (ADR-0047).
2026-10-20 15:59:38 +08:00
Qiu Yuzhou 1d85e86b8e feat(diagnostics): unify log lines into one filtered view
Collapse the four definitions of "one log line" into LogLine (timestamp,
level, source, text) with a single renderer and parser. agent.log lines
now carry the uniform prefix, absorbing the ssxng: event marker into the
source column; legacy lines degrade to raw display without rewriting.
Level detection moves into the row model with a classic three-level
taxonomy whose unified-log projection stays unchanged.

The diagnostics log view merges GUI events and the agent.log tail through
DiagnosticLogIntake into one time-ordered list with level and source
filters; the tail window logic moves into the intake and blank lines are
kept for raw fidelity. The report whitelist projection and unified-log
routing are unchanged.
2026-10-20 14:42:51 +08:00
Qiu Yuzhou 1541e97fb3 test(runtime): silence unified-log writes during test runs
Add the SSXNG_SYSTEM_LOG=0 test seam: RuntimeLog.emit and the sslocal
unified-log destination skip their os.Logger writes when it is set,
while stderr lines and the event sink are unaffected. Test execution
no longer floods the unified log store (a full validate run previously
recorded 795 entries under the shared subsystem, 743 from xctest and
52 from test-spawned agents).

The switch is injected by the schemes' test actions only (Run/Profile
keep system logging so debugging from Xcode still shows it), and set
explicitly in the fixtures that spawn the agent/sslocal processes,
including the fixture that strips inherited SSXNG_* path seams. The
main scheme is now declared explicitly to keep the variable out of the
Launch/Profile actions. Document the seam in ADR-0045.
2026-10-20 12:38:21 +08:00
Qiu Yuzhou b6817568f3 docs(adr): record unified log routing decision
Record ADR-0045: unified-log routing through the emission seam with
native os.Logger and no third-party logging library, the closed-enum
dispatch with public/private text split, and the sslocal output intake
as a second free-form stream that never enters RuntimeLogEvent. Point
the systemLogRoute comment at the ADR.
2026-10-20 12:24:34 +08:00
Qiu Yuzhou e0e2814b49 refactor: separate configuration, runtime, and log directories 2026-10-19 20:17:10 +08:00
Qiu Yuzhou 766d1a3d86 refactor(runtime): make deployment metadata references explicit 2026-10-19 19:59:23 +08:00
Qiu Yuzhou 8a53575d79 docs: shorten READMEs and extract topic guides 2026-10-19 19:42:38 +08:00
Qiu Yuzhou 12ae000cb0 refactor(runtime): separate sslocal output from GUI intent 2026-10-19 19:00:43 +08:00
Qiu Yuzhou 003e40d809 refactor(ng2): encrypt proxy listening configuration 2026-10-19 14:31:18 +08:00
Qiu Yuzhou 0f3454602f refactor: separate proxy preferences from listening configuration 2026-10-19 12:10:48 +08:00
Qiu Yuzhou 985153cd56 refactor(rules): separate rule values from storage models 2026-10-19 09:42:38 +08:00
Qiu Yuzhou e0db8b1e13 refactor(runtime): publish isolated supervision generations
Separate Agent supervision from sslocal input and preserve the latest intent after deployment failure. Persist failure barriers, serialize publication and retirement, and verify reload and single-instance behavior with real processes.

Refs #83
2026-10-19 03:00:33 +08:00
Qiu Yuzhou 507917ab67 refactor(ng2): store secrets directly in server library records 2026-10-18 17:58:30 +08:00
Qiu Yuzhou 6cbef4e57a refactor(connectivity): store test URL in user defaults 2026-10-18 08:31:13 +08:00
Qiu Yuzhou d8a11f7b6b feat: encrypt persistent server library 2026-10-18 07:57:10 +08:00
Qiu Yuzhou 8ca2da494d refactor(runtime): drop ProxyModeError guards and silent desired-proxy skip
Make systemProxyConfiguration total so a runtime document can no longer
swallow derivation via try? and silently skip desired system proxy
convergence. Delete the unreachable ProxyModeError / .mode failure fact
and its presentation; port validity stays at the settings store and the
document contract.
2026-10-17 15:30:47 +08:00
Qiu Yuzhou 3a6d143b17 refactor(settings): separate LAN enablement from listener mode 2026-10-13 21:46:04 +08:00
Qiu Yuzhou 2f0c0d7676 feat: separate local and LAN proxy listeners 2026-10-13 03:58:03 +08:00
Qiu Yuzhou f72c50dceb feat(plugins): configure plugin transport mode 2026-10-11 23:58:47 +08:00
Qiu Yuzhou 84c292eb7f feat(plugin): support aliases and local startup arguments 2026-10-11 21:04:59 +08:00
Qiu Yuzhou e50f671975 feat(servers): add ordered favorites 2026-10-09 23:20:11 +08:00
Qiu Yuzhou f23a24f203 feat(servers): add node creation and modification timestamps
CatalogEntry gains createdAt/updatedAt and the catalog document moves to
v5 (reads v1-v5, writes v5). Modification time is structure-sensitive:
editing a node's own fields bumps the node, a group's direct child set
or order changes bump the group, and a moved node keeps its timestamps.
No-op writes and unchanged subscription refreshes bump nothing; the
refresh rebuild compares per node, so surviving leaves keep theirs. All
times are passed in by callers as now: Date - one moment per logical
commit, the domain layer stays clock-free. Legacy documents load with
nil (unknown, never backfilled); duplicates and imports stamp fresh
times at the moment of the operation.

Semantics are recorded as glossary term 节点时间戳 and ADR 0031. No UI
in this change.
2026-10-09 18:04:48 +08:00
Qiu Yuzhou d06c04246d feat: move diagnostics to a standalone window 2026-10-06 15:20:39 +08:00
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