Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Tests

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: windows-2025
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Build default features
run: cargo build --locked --all-targets
- name: Unit tests
run: cargo test --locked --all-features --all-targets
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project Overview

VCore is a standalone Rust proxy core. The current public contract is Invoke API v5 with internal schema revision 12. Runtime configuration uses the strict schema documented in `docs/config.yaml` and is passed inline as `configYaml` / `configYamls`; YAML contains neither `configVersion` nor `default-proxy`. Public lifecycle state is runtime-local and single-instance.
VCore is a standalone Rust proxy core. The current public contract is Invoke API v5 with internal schema revision 13. Runtime configuration uses the strict schema documented in `docs/config.yaml` and is passed inline as `configYaml` / `configYamls`; YAML contains neither `configVersion` nor `default-proxy`. Public lifecycle state is runtime-local and single-instance.

Apple and Android use host-owned TUN fds through the Unix `rust-tun` adapter. Windows uses `windows-rs` / `Windows.Networking.Vpn`; the packaged ARM64 foreground, AppContainer provider, per-session full-trust runtime, lifecycle, pressure, and packet-channel gates pass on Windows 11. Windows 10, native x64, physical IPv6, WACK, and Store publishing remain release gates. Linux remains unsupported.

Expand All @@ -11,9 +11,9 @@ Current source, tests, and the public contract documents under `docs/` define im
Read the relevant document completely before changing that area:

- FFI, lifecycle, Android protect, or config delivery: `docs/invoke-api.md` and `src/ffi/`.
- YAML, proxy graph, DNS, rules, or sniffer: `docs/config.yaml`, `docs/tun-icmp-dns.md`, and `src/config/`.
- YAML, proxy graph, proxy groups, DNS, rules, or sniffer: `docs/config.yaml`, `docs/tun-icmp-dns.md`, and `src/config/`.
- AnyTLS: `docs/anytls.md`.
- TUN traffic metrics: `docs/controller-api.md`.
- Runtime Controller, proxy-group selection, or TUN traffic metrics: `docs/controller-api.md` and `src/controller.rs`.
- GeoData: `docs/geodata.md`.
- REALITY or the GitHub rustls fork: `docs/reality-wire-protocol.md` and `docs/rustls-reality-release.md`.
- Unix TUN fd ownership or packet I/O: `docs/tun-platform.md`.
Expand All @@ -28,6 +28,7 @@ Read the relevant document completely before changing that area:
- `src/tun_runtime.rs` connects platform raw-IP I/O to `vcore-netstack` and dispatches TCP, UDP, DNS, ICMP, and sniffing.
- `src/platform/` contains platform adapters. Keep Windows callback semantics here instead of simulating a Unix fd.
- `src/dialer.rs` is the shared physical TCP/UDP socket seam. Fix socket protection or Windows `(source IP, interface index)` binding once here rather than in each outbound.
- Static `select` proxy groups are mutable Running Session route targets at the routing `Dispatcher` seam. Keep `dialer-proxy` and `measureDelay` node-only, and do not move group selection into the immutable outbound connector graph.
- `crates/vcore-netstack` is platform-independent raw-IP state and must not depend on WinRT, JNI, Swift, or host UI frameworks.
- Core runtime lifecycle does not infer App, extension, service, or daemon roles and does not implement cross-process state or IPC. The Windows-only host Invoke is the explicit package integration seam for profile/status/Session Snapshot/StartupTask operations and an optional bounded `sessionBackend`; its Session Host owns backend process liveness without interpreting arguments, files, ports, or protocols. Provider runtime state remains process-local.

Expand Down
20 changes: 18 additions & 2 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ The VCore runtime serving one active Windows tunnel session inside the Windows s
_Avoid_: Foreground runtime, provider runtime, external core

**Windows session host**:
The hidden packaged full-trust Application that owns one Windows session runtime and its optional Windows session backend independently of the foreground host.
_Avoid_: Foreground process, provider host, external-core host
The packaged full-trust process registered under the product's single Application that owns one Windows session runtime and its optional Windows session backend independently of the foreground host.
_Avoid_: Helper Application, foreground process, provider host, external-core host

**Windows session backend**:
The optional ordered set of package-local processes whose lifetime is owned by one Windows session host. VCore supervises process liveness but does not interpret their arguments, files, ports, or protocols.
Expand Down Expand Up @@ -51,3 +51,19 @@ _Avoid_: Proxy traffic, transport traffic, per-node traffic
**Local SOCKS5 outbound**:
A normal VCore SOCKS5 outbound to a loopback server. The server may be managed outside VCore or happen to run in a Windows session backend; SOCKS5 readiness and protocol health remain outside the backend contract.
_Avoid_: Managed core, child core, URI configuration

**Proxy node**:
A named, concrete outbound protocol path that can be selected as a route target or referenced by another proxy node through `dialer-proxy`.
_Avoid_: Proxy when a proxy group or route target is intended, server profile

**Proxy group**:
A named route target whose current group selection identifies one direct member route target.
_Avoid_: Folder, provider, subscription

**Route target**:
A proxy node, proxy group, or reserved built-in target that routing rules, the final `MATCH`, and DNS routes can select.
_Avoid_: Proxy when the distinction between a node and group matters, dialer

**Group selection**:
The running-session state identifying the direct member route target currently chosen by a proxy group.
_Avoid_: Variant, configuration switch, active profile
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ windows = { version = "=0.62.2", optional = true, features = [
"Win32_Networking_WinSock",
"Win32_Security",
"Win32_Security_Isolation",
"Win32_System_Com",
"Win32_System_JobObjects",
"Win32_System_RemoteDesktop",
"Win32_System_Threading",
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
English · <a href="./readme/README.zh_CN.md">简体中文</a> · <a href="./readme/README.ru.md">Русский</a>
</p>

VCore is a standalone, host-agnostic Rust client proxy core. It provides proxy graphs, DNS, routing rules, GeoData, an HTTP listener, a TUN data plane, and traffic statistics through strict YAML configuration and Invoke API v5. The internal configuration schema revision is 12; the revision appears only in the `version` response and `buildIdentity`, not in YAML.
VCore is a standalone, host-agnostic Rust client proxy core. It provides proxy graphs, static `select` proxy groups, DNS, routing rules, GeoData, an HTTP listener, a TUN data plane, and a loopback Controller through strict YAML configuration and Invoke API v5. The internal configuration schema revision is 13; the revision appears only in the `version` response and `buildIdentity`, not in YAML.

## Features

- Outbounds: VLESS + XHTTP + TLS/REALITY, SOCKS5 CONNECT/UDP ASSOCIATE, AnyTLS TCP/UoT, and DIRECT.
- Proxy chains: `dialer-proxy` forms a directed acyclic graph of arbitrary length. If node A points to B, the physical path is `client -> B -> A -> target`.
- Proxy groups: static `select` groups keep ordered members, including concrete nodes, nested groups, `DIRECT`, and `REJECT`; their current-session selection can be changed live through the Controller. `dialer-proxy` remains node-only.
- Routing: ordered `DOMAIN`, `DOMAIN-SUFFIX`, `DOMAIN-KEYWORD`, `GEOSITE`, `GEOIP`, `IP-CIDR`, `IP-CIDR6`, `DST-PORT`, `NETWORK`, and final `MATCH` rules.
- DNS: fixed-IP UDP/TCP nameservers, explicit outbounds, ordered policy/failover, typed and opaque caches, singleflight, and TUN UDP/TCP port 53 interception.
- TUN: raw IPv4/IPv6, TCP/UDP, local ICMPv4/ICMPv6 Echo replies, HTTP/TLS/QUIC sniffing, and four per-session traffic counters.
Expand All @@ -23,9 +24,10 @@ VCore is a standalone, host-agnostic Rust client proxy core. It provides proxy g

- YAML is limited to 256 KiB and rejects unknown fields, anchors, aliases, custom tags, and obsolete structures.
- The top level must contain at least one proxy and either `port` or an enabled `tun`.
- Proxy `name` values are case-sensitive and unique; every reference must exist, and the proxy graph must be acyclic.
- `rules` is required and must end with exactly one `MATCH` targeting a real proxy name.
- `DIRECT` and `REJECT` are built-in actions; every other action must be a real proxy name.
- Proxy and group definition names share one exact, case-sensitive namespace. Names are 1–64 UTF-8 bytes, reject surrounding Unicode whitespace, controls, `, # / ? & = % \`, `.` and `..`, and reserve `DIRECT`, `REJECT`, and `RULES`; internal ordinary spaces, CJK, and emoji are allowed.
- `proxy-groups` accepts only `select`. Member order and duplicates are preserved; an omitted `default-selected` selects the first member, while an explicit value must name a direct member. Proxy chains and nested groups must each be acyclic.
- `rules` is required and must end with exactly one `MATCH` targeting a configured proxy node or proxy group.
- `DIRECT` and `REJECT` are built-in actions and group members; every other route target must be a configured proxy node or proxy group. DNS alone also reserves `RULES`.
- Configuration is delivered inline through `configYaml` / `configYamls`; VCore does not read host configuration paths.
- Runtime values such as the Controller, TUN fd, Controller port, and secret are generated by the host and are not stored in user RAW YAML.

Expand Down Expand Up @@ -57,14 +59,18 @@ initialize

`instanceId` is a generation token that is never reused by the current runtime. Commands for the same instance fail fast when another command is active; pure `validateConfig` calls may run concurrently. See [`docs/invoke-api.md`](docs/invoke-api.md) for the complete envelope, methods, fd ownership, and Android protect contract.

TUN traffic is queried through a session-local loopback Controller:
Runtime state is exposed through a session-local loopback Controller:

```http
GET /traffic
GET /group
GET /group/{name}
GET /proxies/{name}
PUT /proxies/{name}
Authorization: Bearer <secret>
```

The response is a one-time `up/down/upTotal/downTotal` snapshot, not a continuous stream. See [`docs/controller-api.md`](docs/controller-api.md).
`GET /traffic` is a one-time `up/down/upTotal/downTotal` TUN snapshot. The group endpoints expose and change the selected direct member of static `select` groups; a successful change affects only new physical TCP, UDP, and DNS transports in the current session. It does not migrate existing connections, UDP associations, DNS state, or pooled TCP transports, and it never performs automatic failover. A Controller that manages groups requires one Bearer secret for all routes and may run without TUN. See [`docs/controller-api.md`](docs/controller-api.md).

## Platforms

Expand Down Expand Up @@ -103,7 +109,7 @@ TCP sessions, ordinary UDP associations, half-open connections, outbound handsha
- [AnyTLS outbound](docs/anytls.md)
- [REALITY V1 client protocol](docs/reality-wire-protocol.md)
- [rustls REALITY dependency and release requirements](docs/rustls-reality-release.md)
- [TUN traffic Controller](docs/controller-api.md)
- [Runtime Controller](docs/controller-api.md)
- [TUN ICMP and DNS](docs/tun-icmp-dns.md)
- [GeoData rules and assets](docs/geodata.md)
- [TUN platform layer](docs/tun-platform.md)
Expand Down
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

## 公共契约

1. [配置协议](config.yaml):配置修订版 12 的完整 YAML 示例和严格字段边界
1. [配置协议](config.yaml):配置修订版 13 的完整 YAML 示例、静态 `select` 代理组和严格字段边界
2. [Invoke API](invoke-api.md):API v5 的请求格式、生命周期和平台回调。
3. [AnyTLS 出站](anytls.md):TLS、会话复用、填充、TCP/UoT 和清理语义。
4. [REALITY V1 协议](reality-wire-protocol.md):握手、认证、连接状态和失败边界。
5. [TUN 流量 Controller](controller-api.md):Bearer 鉴权和四字段流量快照
6. [DNS 与 ICMP](tun-icmp-dns.md):TUN DNS、缓存、故障转移和本地 Echo Reply。
5. [运行时 Controller](controller-api.md):Bearer 鉴权、实时代理组选择和四字段 TUN 流量快照
6. [DNS 与 ICMP](tun-icmp-dns.md):TUN DNS、代理组出口、缓存、故障转移和本地 Echo Reply。
7. [GeoData](geodata.md):规则、资产更新、匹配器和资源上限。
8. [TUN 平台层](tun-platform.md):文件描述符、原始 IP 包和平台所有权。

Expand Down
28 changes: 21 additions & 7 deletions docs/acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
当前测试覆盖以下公共边界:

- Invoke API v5 envelope、严格 payload、单实例生命周期、panic 隔离和同步清理;
- 配置修订版 12、IPv6 总开关、代理图、节点测速配置和未知字段拒绝;
- 配置修订版 13、IPv6 总开关、代理图、静态 `select` 代理组、节点测速配置和未知字段拒绝;
- VLESS/XHTTP/TLS/REALITY、SOCKS5、AnyTLS 和代理链;
- DNS wire、缓存、singleflight、policy、故障转移和 TCP 复用;
- 规则、GeoData、HTTP/TLS/QUIC 嗅探;
- ICMPv4/ICMPv6 Echo、校验和、分片、MTU 和队列满;
- Apple/Android TUN 帧格式、文件描述符副本和关闭所有权;
- Windows 控制/数据协议、Session Snapshot v2、会合记录、包队列、批量写入、物理网络绑定和 Job Object 多进程监督;
- Controller 鉴权、速率和累计流量语义。
- Windows 单 Application manifest、Provider/Session Host token 绑定、控制/数据协议、Session Snapshot v2、会合记录、包队列、批量写入、物理网络绑定和 Job Object 多进程监督;
- Controller 鉴权、速率和累计流量语义,以及代理组查询、实时选择和有界请求处理

常用命令:

Expand Down Expand Up @@ -70,6 +70,7 @@ Windows 11 ARM64 开发验收的环境、命令、结果和适用范围保存在
| DNS / rules / GeoData / sniffer | 已覆盖 | 本地 DNS 与代理 fixture | Windows ARM64 已覆盖 |
| ICMPv4 / ICMPv6 Echo | 已覆盖 | 不适用 | Windows ARM64 已覆盖 |
| Controller 四字段流量 | 已覆盖 | HTTP fixture | Windows ARM64 已覆盖 |
| Controller `select` 代理组控制 | 已覆盖 | HTTP fixture | 物理设备未验证 |

“本地互操作”只证明当前双方在受控配置下可以通信,不代表所有公网服务或配置组合。

Expand All @@ -93,7 +94,20 @@ Windows 11 ARM64 开发验收的环境、命令、结果和适用范围保存在

## Windows VPN

已验证范围限于 Windows 11 ARM64 开发签名安装包:
单 Application 可行性在当前 Windows 11 ARM64 build 26200.9278 机器的 Developer Mode loose-package spike 上验证。该 spike 以 `042919ab5ead6af719ae68564244966e95003b58` 为基线,并包含后来收敛为 `d9018a2dfb75ab1f55c593023cbaa60951165eb5` 的未提交目标改动;基线 SHA 本身不能复现该结果。实际执行路径为:

```powershell
Add-AppxPackage -Register <stage>\AppxManifest.xml
vcore-uwp-demo.exe environment
vcore-uwp-demo.exe status
vcore-uwp-demo.exe start <demo.yaml>
vcore-uwp-demo.exe status
vcore-uwp-demo.exe stop
```

观察结果是 manifest 只有一个 Application,Provider 是 AppContainer,Provider 通过无参数 `FullTrustProcessLauncher` 启动具有同一 package identity 的 medium-integrity Session Host,connect/stop 与 rendezvous 清理通过。该记录只证明本机可行性,不作为其他系统、架构、签名包、WACK 或 Store 的验证结论。

此前 Windows 11 ARM64 开发签名包的数据面证据覆盖:

- `Windows.Networking.Vpn` Provider 激活;
- 完全信任前台宿主、AppContainer Provider 和完全信任 Session Host 的进程边界;
Expand All @@ -103,13 +117,13 @@ Windows 11 ARM64 开发验收的环境、命令、结果和适用范围保存在
- 非回环 socket 的物理源地址与接口索引绑定;
- Controller、外部回环 SOCKS5 和前台宿主退出后的会话延续;
- Provider/Session Host 退出、管道错误和网络变化时的失败关闭;
- 快速重连、持续压力、零队列丢弃和显式 Stop 清理;
- 断开状态下的安装包原位升级和签名校验。
- 快速重连、持续压力、零队列丢弃和显式 Stop 清理。

Windows 路由必须保留两条 `/1`。在安装包环境中,单条 VPN `/0` 会使按产品要求绑定物理源地址和接口的外层 socket 返回 `WSAENETUNREACH`;两条 `/1` 不会产生该问题。

尚未验证或完成
以下项目由发布开发者在对应机器或服务中验证,不阻塞上述本机可行性结论

- 单 Application test-signed MSIX 安装;
- Windows 10 20H2;
- 原生 x64 Windows;
- 真实物理 IPv6;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: accepted
status: superseded by ADR-0007
---

# ADR 0003:在完全信任的 Session Host 中运行 Windows 会话
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
status: accepted
---

# ADR 0005:将 select 代理组建模为会话路由目标

VCore 把首版 `select` 代理组建模为位于不可变代理节点与 `dialer-proxy` 图之上的命名路由目标。组拥有一个有序、非空的直接成员列表,成员可以是代理节点、嵌套 `select` 组、`DIRECT` 或 `REJECT`;省略 `default-selected` 时选择第一项,显式值必须精确命中一个直接成员。普通规则、最终 `MATCH` 和 DNS 路由可以引用组,`dialer-proxy` 与 `measureDelay` 仍然只接受代理节点。

组选择只属于当前 VCore 运行会话,由调用方在下一次配置中重新提供。每次 `connect_tcp` 或 `open_datagram` 进入组时只读取一次选择;成功切换只影响提交后才进入组的新调用,不迁移已有 TCP、不替换已有 UDP transport,也不清理 DNS cache 或已有 DNS TCP connection。已经读取旧选择但仍在建链的调用继续使用旧成员;被选成员失败时原样返回错误,不在组内隐式 failover。

代理组位于 `Dispatcher` seam,具体协议节点及其 `dialer-proxy` graph 继续由 `OutboundConnector` 实现。这个边界避免让运行时选择渗入协议建链,并使“组不能作为 `dialer-proxy`”成为明确、可验证的约束。
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
status: accepted
---

# ADR 0006:只通过 Controller 控制代理组的实时选择

VCore 首版只通过 Controller 暴露代理组的实时选择,不新增 Invoke 方法,也不扩展 Windows 控制管道。Controller 采用 mihomo `select` 的核心调用形状:`GET /group`、`GET /group/{name}`、`GET /proxies/{name}` 和 `PUT /proxies/{name}`;PUT body 为 `{"name":"member"}`,成功返回空的 `204`。组状态只承诺 `name`、`type`、`all`、`now`,不宣称兼容完整 mihomo Dashboard API。

Windows 的实际运行时位于独立 Session Host,Apple 与 Android 的运行时位于 Invoke owner 背后;Controller 是现有平台中唯一能统一到达实际运行会话的 Adapter。组路由可以在无 TUN 的本地代理运行时中使用,`/traffic` 仍只在存在 TUN 统计时提供。

配置同时包含代理组和 Controller 时,必须为整个 Controller 配置 Bearer secret。请求使用精确名称和严格、有界的 JSON,任何失败都不得改变选择。没有 Controller 的代理组配置仍然有效,但本次会话中没有公开的实时切换入口;调用方在成功切换后自行持久化下一次启动所需的 `default-selected`,持久化失败不回滚已经成功的运行时选择。
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
status: accepted
---

# Activate the Windows Session Host from the VPN Provider

A package has one Application but keeps separate full-trust foreground, AppContainer Provider, and full-trust Session Host processes. The Provider launches the Session Host through parameterless `FullTrustProcessLauncher`; the rendezvous token remains untrusted until the Provider binds it to the profile token during the existing handshake, preserving Windows 10 support without another protocol or compatibility path.
Loading
Loading