[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-94788":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":15,"forks30d":15,"starsTrendScore":15,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":20,"hasPages":20,"topics":22,"createdAt":9,"pushedAt":9,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":15,"starSnapshotCount":15,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},94788,"wificalling-location-gateway","smthdagg\u002Fwificalling-location-gateway","smthdagg","Fail-open OpenWrt WLOC location gateway PoC with auditable multi-agent development workflow",null,"Rust",162,36,1,5,0,3,8,47,"MIT License",false,"main",[],"2026-08-24 04:01:22","# Wi‑Fi Calling Location Gateway\n\n\u003Cdiv align=\"center\">\n\n**An all-in-one Wi‑Fi Calling + Apple WLOC gateway for OpenWrt \u002F ImmortalWrt**\n\nWithout touching the stable data plane of Wi‑Fi Calling Gateway 1.7, a standalone Rust service handles exit geolocation, WLOC response rewriting, certificate lifecycle, precise traffic isolation, and LuCI management.\n\n[![CI](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Factions\u002Fworkflows\u002Fci.yml)\n[![Release](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frelease-v1.0.2-blue.svg)](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Freleases\u002Ftag\u002Fv1.0.2)\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-green.svg)](LICENSE)\n[![Rust 1.90](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FRust-1.90-orange.svg?logo=rust)](Cargo.toml)\n[![OpenWrt](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FOpenWrt-24.10%20%7C%2025.12-00B5E2.svg?logo=openwrt)](#support-and-validation-status)\n[![GitHub stars](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002Fsmthdagg\u002Fwificalling-location-gateway?style=flat&logo=github)](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Fstargazers)\n[![LINUX.DO](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLINUX.DO-community-1f1f1f)](https:\u002F\u002Flinux.do\u002F)\n\n[English Guide](docs\u002FWIFICALLING_WLOC_TUTORIAL_EN.md) · [中文完整教程](docs\u002FWIFICALLING_WLOC_TUTORIAL_ZH.md) · [Security Policy](SECURITY.md) · [Development & Test Plan](DEVELOPMENT_TEST_PLAN.md)\n\n\u003C\u002Fdiv>\n\n> [!IMPORTANT]\n> This project is intended for authorized devices, networks, and test environments only. It does not prove that your carrier has enabled Wi‑Fi Calling, and it is not a substitute for real call verification; WLOC target locations must never be treated as emergency-call location. Follow local law, carrier terms, and Apple device-management requirements.\n\n![WLOC settings UI](docs\u002Fimages\u002Fwificalling-wloc\u002F04-wloc-settings.png)\n\n---\n\n## English\n\n## Introduction\n\nWi‑Fi Calling Location Gateway brings two previously separate flows together on one router:\n\n1. **Wi‑Fi Calling Gateway 1.7** selects a sing-box node for designated LAN devices and keeps the ePDG\u002FIPsec channel (UDP 500\u002F4500) independent.\n2. **The WLOC service** handles only TCP 443 traffic from the designated test device to the Apple WLOC hosts. In auto mode it resolves the target region from the exit IP of the node bound to that device; in manual mode it uses administrator-chosen coordinates.\n3. **The LuCI interface** provides nodes, device policies, auto\u002Fmanual location, certificate installation, runtime status, and a sanitized event log.\n\nThe core boundary of the project is \"**independent, precise, and revertible**\": WLOC uses its own process, UCI config, Unix socket, nftables table, and logs. It never takes over the Wi‑Fi Calling Gateway 1.7 table and never intercepts UDP 500\u002F4500. When the protocol is unknown, Geo data is invalid, or the service is unhealthy, no default fake coordinates are produced.\n\n## Features\n\n- Statically linked Rust daemon optimized for OpenWrt musl targets and small release size.\n- Auto-follows the country, city, timezone, and coordinates of the node bound to the device; after a node switch the monitor follows within about 10 seconds, and a one-click \"Refresh IP\" button re-probes immediately.\n- Manual place search, latitude\u002Flongitude entry, and saved location presets.\n- The certificate link, DNS hijack, and TPROXY rules are generated from the router's actual LAN IP at runtime — no more hardcoded 192.168.31.x, so any LAN subnet works out of the box.\n- Locally generated, persisted WLOC root CA with an iPhone `.mobileconfig` install entry and fingerprint verification.\n- The \"Add LAN device\" dialog lists connected LAN devices (DHCP leases + ARP cache); picking one fills in the device name and the real IP automatically.\n- Bounded TLS, HTTP\u002F2, and WLOC protocol handling; upstream certificate and hostname verification is never downgraded.\n- DNS\u002Fnftables isolation scoped to \"designated device + authorized hosts + TCP 443\".\n- Root-only Unix socket control API with an rpcd-authorized LuCI bridge.\n- Wi‑Fi Calling tunnel status, WLOC current target, and sanitized event log.\n- WireGuard nodes are fully supported: pre-shared keys, standard\n  `[Interface]`\u002F`[Peer]` config import, real-handshake health checks, and\n  WLOC follow-device exit probing through sing-box endpoints.\n- Per-node **nodeTest** button: run a fresh connection test on demand -\n  a real WireGuard handshake (bypassing the monitor's result cache) or a\n  TCP reachability probe for other protocols - with the verified exit IP\n  or a classified failure reason (missing config \u002F timeout \u002F unreachable)\n  in a banner that stays until closed.\n- A dedicated **Service Status** page (Services > Service Status) reports\n  both services at a glance - daemon processes, config validity, nftables\n  rules, build patches, and node health - refreshed every 10 seconds.\n- IPK (OpenWrt 24.10 \u002F iStoreOS 24.10) and native APK v3 (OpenWrt 25.12) packaging.\n- Pinned SDK\u002Ftoolchain digests, offline locked builds, dependency audit, coverage gate, and Docker boot verification.\n\n## How it works\n\n```mermaid\nflowchart LR\n    I[\"Authorized test iPhone\"] -->|\"Wi‑Fi Calling · UDP 500\u002F4500\"| G[\"Wi‑Fi Calling Gateway 1.7\"]\n    G --> N[\"Bound sing-box node\"]\n    I -->|\"Apple WLOC · TCP 443\"| D[\"Precise DNS \u002F nftables isolation\"]\n    D --> R[\"wloc-service · Rust\"]\n    R -->|\"TLS + HTTP\u002F2, strict upstream validation\"| A[\"Apple WLOC\"]\n    R --> P[\"Exit probe\"]\n    P --> N\n    P --> X[\"Geo resolution & cache\"]\n    X --> R\n    L[\"LuCI \u002F rpcd\"] -->|\"root-only UDS API\"| R\n```\n\nA location update roughly goes through these steps:\n\n1. The router feeds only the assigned test device's Apple WLOC requests into the standalone service.\n2. Auto mode probes the real exit through the device's bound sing-box node; manual mode reads locally stored coordinates.\n3. The Geo layer validates country code, coordinate ranges, timezone, expiry, and provider responses — it never fabricates a result when data is unavailable.\n4. The service rewrites a response only when the authorized protocol structure, resource limits, TLS\u002FALPN, and safety state all hold; otherwise it passes the original response through or withdraws the redirect.\n5. LuCI shows the target location and network evidence; raw WLOC responses, node credentials, call content, and message content are never logged.\n\nMore detail: [WLOC Service API](docs\u002Fapi\u002FWLOC_SERVICE_API.md), [Threat model](docs\u002Fsecurity\u002Fthreat-model.md), and [fail-open constraints](docs\u002Fsecurity\u002Ffail-open.md).\n\n## Implementation\n\n| Layer | Implementation | Key constraints |\n|---|---|---|\n| Service runtime | Rust 2021, Tokio, static musl ELF | Rust 1.90; release LTO, `opt-level=z`, panic abort |\n| TLS \u002F HTTP | rustls, ring, tokio-rustls, h2 | TLS 1.2\u002F1.3, ALPN `h2`, strict upstream cert & hostname validation |\n| WLOC protocol | standalone clean-room protocol model with bounded parsing | Unknown, malformed, or oversized content is never guessed or partially rewritten |\n| Control plane | `wloc.service\u002Fv1`, 4-byte BE framing, JSON, Unix socket | 16 KiB max frame, 2s total timeout, socket 0600, no TCP management port |\n| Exit & location | sing-box exit probe, Geo primary\u002Ffallback + cache, manual coordinates | Invalid or stale data never falls back to default coordinates |\n| OpenWrt integration | procd, UCI, rpcd, dnsmasq, firewall4\u002Fnftables | WLOC keeps its own table; never touches the Gateway table or UDP 500\u002F4500 |\n| Admin UI | LuCI JavaScript | Auto\u002Fmanual switch, certificate, status, and log; sensitive fields sanitized |\n| Build & release | OpenWrt SDK \u002F Docker images pinned by digest | locked\u002Foffline compile, SHA-256, architecture tag cannot masquerade as `all` |\n\n## Support and validation status\n\n\"Installable\" is not the same as \"verified with a real iPhone \u002F Wi‑Fi Calling\". The table separates the evidence levels:\n\n| Platform | Arch | Package manager | Current evidence | Status |\n|---|---:|---|---|---|\n| Redmi AX6S · ImmortalWrt 24.10.6 | MediaTek MT7622 \u002F AArch64 | opkg | Official AArch64 OpenWrt 24.10.5 Docker install\u002Fstart matrix; plus real-device procd, LuCI, auto\u002Fmanual switch, certificate, and iPhone WLOC path | **Docker + real device passed** |\n| OpenWrt 24.10.8 | x86_64 | opkg \u002F IPK | Docker boot of init\u002Fubus, integrated package install, service start, socket and v1 status checks | **Install matrix passed** |\n| iStoreOS 24.10.5 | x86_64 | opkg \u002F IPK | Same as above | **Install matrix passed** |\n| OpenWrt 25.12.3 | x86_64 | apk \u002F APK v3 | Same, using native APK v3, not a renamed IPK | **Install matrix passed** |\n| Other OpenWrt \u002F ImmortalWrt versions or CPUs | — | — | No device\u002FSDK evidence yet | **Not verified** |\n\nThe runtime packages contain a Rust ELF and **must match the router CPU architecture**; only the LuCI package is `all`\u002F`noarch`. x86_64 packages are built with a pinned SDK; AX6S uses a separate AArch64 `cortex-a53` cross toolchain. The formal Docker matrix installs all three release assets. Docker verifies install and boot, not nftables, DNS, carrier, or iPhone end-to-end behavior.\n\n## Installation\n\n### Prerequisites\n\n- sing-box, firewall4\u002Fnftables, LuCI, and rpcd available.\n- A fixed DHCP address for the test iPhone and a correct node binding in the Gateway.\n- Router config backed up; WARP, Shadowrocket, or any other VPN on the phone stays off during router WLOC testing.\n- Install this project's CA only on the dedicated test device and verify the certificate fingerprint.\n\n### 1. Choose the right package\n\nThe Redmi AX6S uses a single architecture-specific integrated package:\n\n- `wificalling-location-gateway_\u003Cversion>_aarch64_cortex-a53.ipk`\n\nIt bundles Wi‑Fi Calling Gateway 1.7, the WLOC service, control tools, and the unified LuCI; installing `luci-app-wificalling-gateway` or `wloc-service` separately is not required. On reinstall or upgrade, opkg preserves `\u002Fetc\u002Fconfig\u002Fwificalling-gateway` and `\u002Fetc\u002Fconfig\u002Fwloc-service`.\n\nSince the formal 1.0 line, every platform gets exactly one complete integrated package named\n`wificalling-location-gateway` — Wi‑Fi Calling Gateway 1.7, WLOC service, control tools, and unified LuCI in one; users no longer install component packages separately.\n\nTwo ways to install:\n\n**Method A — package feed (recommended)**: add the signed feed and `opkg install` directly:\n\n```sh\n# Import the feed signing key (one-time)\nwget -O \u002Fetc\u002Fopkg\u002Fkeys\u002Ff7050198aa77cf15 \\\n  https:\u002F\u002Fraw.githubusercontent.com\u002Fsmthdagg\u002Fwificalling-location-gateway-feed\u002Fmain\u002Fwloc.pub\n# Add the feed and install\necho \"src\u002Fgz wloc https:\u002F\u002Fsmthdagg.github.io\u002Fwificalling-location-gateway-feed\" \\\n  >> \u002Fetc\u002Fopkg\u002Fcustomfeeds.conf\nopkg update && opkg install wificalling-location-gateway\n```\n\n**Method B — manual download**: grab the matching file from\n[Releases](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Freleases)\nand verify it against `SHA256SUMS` from the same release directory first.\n\nFull instructions for both methods live in the\n[feed repository](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway-feed)\n(including the manual `.apk` install commands for OpenWrt 25.x).\n\n### 2. Redmi AX6S (single integrated IPK)\n\n```sh\nopkg install \u002Ftmp\u002Fwificalling-location-gateway_\u003Cversion>_aarch64_cortex-a53.ipk\n```\n\nDo not run `opkg remove` first; installing directly restores missing components and keeps the existing configuration. After installing, check both services under \"Verify the services\" below.\n\n### 3. OpenWrt 24.10 \u002F iStoreOS 24.10 (IPK)\n\n```sh\nopkg install \u002Ftmp\u002Fwificalling-location-gateway_1.2.0-r1_x86_64.ipk\n```\n\n### 4. OpenWrt 25.12 (native APK v3)\n\n```sh\napk add --allow-untrusted \u002Ftmp\u002Fwificalling-location-gateway-1.2.0-r1.apk\n```\n\n`--allow-untrusted` applies only to locally built packages that are not yet signed in a repository. Formal releases use repository signing; never rename an IPK into an APK.\n\n### 5. Verify the services\n\n```sh\ntest -S \u002Fvar\u002Frun\u002Fwloc-service\u002Fcontrol.sock\n\u002Fusr\u002Fsbin\u002Fwloc-ctl status\n\u002Fetc\u002Finit.d\u002Fwificalling-gateway status\nlogread -e wloc-service\n```\n\nThe status response must contain `\"api_version\":\"wloc.service\u002Fv1\"`. If the LuCI menu did not refresh, clear the browser cache and log back in instead of reinstalling packages for other architectures.\n\n## Usage order\n\nConfigure in this order to avoid mixing network and location problems:\n\n1. Import or add nodes in **Wi‑Fi Calling Settings**, then Save & Apply.\n2. In **Device Policies**, add the test iPhone with a fixed LAN IP, routing mode, and bound node; Save & Apply again.\n3. Enable Wi‑Fi Calling on the iPhone and watch for UDP 4500 `ASSURED` in **Wi‑Fi Calling Monitor & Log**; always confirm with a real call in\u002Fout.\n4. In **WLOC Settings**, copy the router-generated profile link and install it from Safari on the iPhone.\n5. On the iPhone, enable full trust for `wloc-service root CA` under Settings → General → About → Certificate Trust Settings, and verify the fingerprint.\n6. Turn on WLOC interception and choose **Auto (follow node)** or a manual location; Save & Apply.\n7. Toggle airplane mode \u002F Wi‑Fi or reopen Maps\u002FWeather to trigger a location request.\n8. Check the mode, country, city, timezone, coordinates, Geo state, and update time in **WLOC Monitor & Log**.\n\nStep-by-step guides:\n\n- [Wi‑Fi Calling + WLOC Complete User Guide (English)](docs\u002FWIFICALLING_WLOC_TUTORIAL_EN.md)\n- [Wi‑Fi Calling + WLOC 中文完整使用教程](docs\u002FWIFICALLING_WLOC_TUTORIAL_ZH.md)\n- [AX6S deployment and real-device validation record](docs\u002Fdeployment\u002FAX6S_DEPLOYMENT.md)\n\n## Building and verifying from source\n\n### Rust quality gate\n\n```sh\n.\u002Fscripts\u002Fci\u002Fverify.sh\n```\n\nThis entry runs formatting, Clippy, unit\u002Fintegration tests, Rust line coverage (minimum 80%), dependency audit, license policy, secret scan, release size, and repository contract checks. The formal 1.0 baseline is **69 Python tests passing, Rust line coverage ≥ 80%, and a release verification binary of about 0.97 MB**.\n\n### AX6S \u002F AArch64 cross build\n\n```sh\nOPENWRT_BIN_NAME=wloc-service \\\nOPENWRT_CROSS_CACHE_DIR=\u002Ftmp\u002Fwloc-rust-openwrt \\\n.\u002Fscripts\u002Fci\u002Fverify-rust-openwrt.sh\n```\n\nThis pins the OpenWrt 24.10.8 `mediatek\u002Fmt7622` toolchain, Rust version, and SHA-256, and verifies the AArch64 ELF, static linking, and size. See [Rust OpenWrt cross-build notes](docs\u002Ftesting\u002FRUST_OPENWRT_CROSS_BUILD.md).\n\n### x86_64 dual-format packaging\n\n```sh\n.\u002Fscripts\u002Fopenwrt\u002Fbuild-x86_64-runtime.sh \\\n  --out-dir \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\"\n\n.\u002Fscripts\u002Fopenwrt\u002Fbuild-release-packages.sh \\\n  --version 1.0.2 \\\n  --release 1 \\\n  --arch x86_64 \\\n  --service-bin \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\u002Fwloc-service\" \\\n  --ctl-bin \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\u002Fwloc-ctl\" \\\n  --gateway-ipk \u002Fabsolute\u002Fpath\u002Fluci-app-wificalling-gateway_1.7.3-1_all.ipk \\\n  --gateway-sha256 \u003Cverified-sha256> \\\n  --out-dir \"$PWD\u002Fdist\u002Fopenwrt-release\"\n```\n\n### Four-environment Docker install & start matrix for all release packages\n\n```sh\n.\u002Fscripts\u002Fopenwrt\u002Fverify-docker-matrix.sh \\\n  --dist-dir \"$PWD\u002Fdist\u002Fv1.0.2\"\n```\n\nBuilds use the official OpenWrt SDK pinned by digest; after dependency preparation, product compilation runs locked\u002Foffline with read-only sources in a network-disabled container. Full boundaries and results: [OpenWrt packaging and Docker matrix](docs\u002Ftesting\u002FOPENWRT_PACKAGE_DOCKER_MATRIX.md).\n\n## Language composition\n\nA GitHub Linguist byte snapshot of the current main branch (2026-08-13). Python mostly drives reproducible builds, fixture governance, and CI; the router product runtime is mainly Rust, with Shell handling OpenWrt lifecycle and network integration.\n\n```mermaid\npie showData\n    title GitHub Linguist language snapshot\n    \"Python · 59.03%\" : 63959\n    \"Rust · 22.40%\" : 24273\n    \"Shell · 18.56%\" : 20112\n```\n\n> The numbers drift as main updates; whether LuCI JavaScript, docs, and generated\u002Fexcluded files count depends on GitHub Linguist rules. Do not judge the project's primary language by helper-tool bytes alone.\n\n## Project structure\n\n```text\nsrc\u002F                         Rust service, protocol, TLS\u002FH2, exit & Geo modules\nopenwrt\u002F                     procd\u002FUCI, LuCI\u002Frpcd, and OpenWrt package definitions\nscripts\u002Fopenwrt\u002F             cross builds, dual-format packaging, Docker matrix\nscripts\u002Fci\u002F                  coverage, security, dependency, and repository gates\ntests\u002F                       Rust, Python, JavaScript, and network-model tests\nfixtures\u002F                    synthetic\u002Fsanitized fixture contracts and validators\ndocs\u002F                        API, security, deployment, testing, bilingual guides\n.handoffs\u002F                   reproducible multi-agent handoff records\n```\n\n## Security, privacy, and rollback\n\n- The CA private key lives only on the router (mode 0600); it must never be committed to Git, support packages, or logs.\n- No node secrets, tokens, raw captures, device identifiers, precise user locations, or raw WLOC responses are committed.\n- Only the designated test device and an explicit host scope are allowed; normal HTTPS, other LAN devices, and UDP 500\u002F4500 are not part of the WLOC data plane.\n- When upstream validation, ALPN, resource limits, or Geo checks fail, the service must not keep running with a \"looks successful\" default location.\n- On disable, the WLOC redirect is withdrawn first, then the engine is drained and stopped; before recovery, confirm the standalone nftables rules are gone.\n- Deleting the `wloc-service root CA` profile on the iPhone revokes device trust; after regenerating the CA, reinstall and verify the new fingerprint on every test device.\n\nReport vulnerabilities privately via [SECURITY.md](SECURITY.md); never paste certificates, IPs, node configs, or device information into public issues.\n\n## Contributing\n\nThis repository uses GitHub Issues as the only assignable work units, integrated through dedicated branches, path leases, reproducible handoffs, and cross-role reviews. Before committing:\n\n1. Read [AGENTS.md](AGENTS.md) and the owned paths of the corresponding issue;\n2. Write a failing test first, then the minimal implementation;\n3. Run `.\u002Fscripts\u002Fci\u002Fverify.sh`;\n4. Review the diff for secrets, keys, device data, and unrelated changes;\n5. Merge through a Pull Request; security-sensitive changes must not be self-reviewed.\n\nDetailed collaboration: [Multi-agent workflow](docs\u002FMULTI_AGENT_WORKFLOW.md).\n\n## Star growth\n\n[![Star History](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002Fsmthdagg\u002Fwificalling-location-gateway@star-chart\u002Fdocs\u002Fimages\u002Fstar-history.svg?v=2)](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Fstargazers)\n\n> The chart is regenerated daily by the `star-history-chart` workflow (or manually from the Actions tab): it reads the official star timeline with GitHub's auto-injected token and renders the SVG locally — the token is never written to any repository file and no third-party service is involved. `main` is branch-protected and the chart lives on the `star-chart` branch, embedded via the jsDelivr CDN; stars earned before GitHub exposed the timeline (the initial period) do not appear.\n\nIf this project helps your OpenWrt \u002F Wi‑Fi Calling experiments, a Star, a reproducible bug report, or a note in the [LINUX.DO](https:\u002F\u002Flinux.do\u002F) community is welcome. Please never publish personal locations, certificates, or proxy credentials in public content.\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE). Third-party dependencies and external projects remain under their own licenses; the MIT grant does not change the isolation requirements for external AGPL implementation material defined in the [clean-room boundary ADR](docs\u002Fadr\u002F0001-license-boundary.md).\n\nWi‑Fi Calling Gateway 1.7 continues to be maintained by its own repository. This repository does not vendor its source; formal builds accept only published IPKs validated by identity, version, and SHA-256, and combine them into a single installable package at build time.\n\n---\n\n## 中文\n\n## 项目简介\n\nWi‑Fi Calling Location Gateway 将两个原本分离的流程组织在同一台路由器上：\n\n1. **Wi‑Fi Calling Gateway 1.7** 为指定局域网设备选择 sing-box 节点，并保持 UDP 500\u002F4500 的 ePDG\u002FIPsec 通道独立运行。\n2. **WLOC 服务**只处理指定测试设备发往 Apple WLOC 主机的 TCP 443 流量；自动模式根据该设备绑定节点的出口 IP 解析目标地区，手动模式使用管理员选择的坐标。\n3. **LuCI 界面**提供节点、设备策略、自动\u002F手动位置、证书安装、运行状态和脱敏日志入口。\n\n项目的核心边界是“**独立、精确、可回退**”：WLOC 使用自己的进程、UCI 配置、Unix Socket、nftables 表和日志，不接管 Wi‑Fi Calling Gateway 1.7 的表，也不拦截 UDP 500\u002F4500。遇到未知协议、无效地理数据或服务异常时，不生成默认虚假坐标。\n\n## 主要能力\n\n- Rust 静态守护进程，针对 OpenWrt 的 musl 环境和小体积发布配置优化。\n- 自动跟随设备所绑定代理节点的出口国家、城市、时区和坐标；切换设备节点后约 10 秒内自动跟随，监控页也可一键“刷新 IP”立即重探测。\n- 手动地点搜索、经纬度输入和常用位置预设。\n- 证书与拦截全程适配任意局域网网段：证书链接、DNS 劫持和 TPROXY 规则按路由器实际 LAN IP 动态生成，不再写死 192.168.31.x。\n- 本地生成并持久化 WLOC 根证书，提供 iPhone `.mobileconfig` 安装入口与指纹核验。\n- “添加局域网设备”弹窗自动列出局域网内已连接设备（DHCP 租约 + ARP），选择后自动填入设备名称与真实 IP。\n- 有界 TLS、HTTP\u002F2 和 WLOC 协议处理；上游证书与主机名验证不降级。\n- 精确到“指定设备 + 授权主机 + TCP 443”的 DNS\u002Fnftables 隔离。\n- root-only Unix Socket 控制 API，以及经 rpcd 授权的 LuCI 管理桥接。\n- Wi‑Fi Calling 隧道状态、WLOC 当前目标与脱敏事件日志。\n- 每个节点提供 **nodeTest** 测试按钮：随时执行一次新的连接测试——WireGuard 节点进行真实握手（绕过监控循环的结果缓存），其他协议执行 TCP 连通性探测；结果显示出口 IP 或分类失败原因（配置缺失 \u002F 超时 \u002F 不可达），横幅带关闭按钮且不会自动消失。\n- IPK（OpenWrt 24.10 \u002F iStoreOS 24.10）与原生 APK v3（OpenWrt 25.12）打包。\n- 固定 SDK\u002F工具链、离线锁定编译、依赖审计、覆盖率门禁和 Docker 启动验证。\n\n## 工作原理\n\n```mermaid\nflowchart LR\n    I[\"授权测试 iPhone\"] -->|\"Wi‑Fi Calling · UDP 500\u002F4500\"| G[\"Wi‑Fi Calling Gateway 1.7\"]\n    G --> N[\"绑定的 sing-box 节点\"]\n    I -->|\"Apple WLOC · TCP 443\"| D[\"精确 DNS \u002F nftables 隔离\"]\n    D --> R[\"wloc-service · Rust\"]\n    R -->|\"TLS + HTTP\u002F2，上游严格校验\"| A[\"Apple WLOC\"]\n    R --> P[\"出口探测\"]\n    P --> N\n    P --> X[\"Geo 解析与缓存\"]\n    X --> R\n    L[\"LuCI \u002F rpcd\"] -->|\"root-only UDS API\"| R\n```\n\n一次位置更新大致经历以下步骤：\n\n1. 路由器只把已分配测试设备的 Apple WLOC 请求送入独立服务。\n2. 自动模式经该设备绑定的 sing-box 节点探测真实出口；手动模式读取本地保存的坐标。\n3. Geo 层对国家码、坐标范围、时区、有效期和提供方响应进行校验，不可用时不伪造结果。\n4. 服务仅在授权协议结构、资源限制、TLS\u002FALPN 和安全状态全部满足时处理响应；否则转发原始响应或撤销重定向。\n5. LuCI 展示目标位置与网络证据，不记录原始 WLOC 响应、节点凭据、通话内容或短信内容。\n\n更详细的接口与安全设计见 [WLOC Service API](docs\u002Fapi\u002FWLOC_SERVICE_API.md)、[威胁模型](docs\u002Fsecurity\u002Fthreat-model.md) 和 [fail-open 约束](docs\u002Fsecurity\u002Ffail-open.md)。\n\n## 技术实现\n\n| 层级 | 实现 | 关键约束 |\n|---|---|---|\n| 服务运行时 | Rust 2021、Tokio、静态 musl ELF | Rust 1.90；release LTO、`opt-level=z`、panic abort |\n| TLS \u002F HTTP | rustls、ring、tokio-rustls、h2 | TLS 1.2\u002F1.3、ALPN `h2`、上游证书与主机名强校验 |\n| WLOC 协议 | 独立 clean-room 协议模型与有界解析 | 未知、畸形、超限内容不猜测、不部分修改 |\n| 控制面 | `wloc.service\u002Fv1`、4-byte BE 帧、JSON、Unix Socket | 最大 16 KiB、总超时 2 秒、Socket 0600、无 TCP 管理端口 |\n| 出口与位置 | sing-box 出口探测、Geo 主备\u002F缓存、手动坐标 | 无效或过期数据不回落到默认坐标 |\n| OpenWrt 集成 | procd、UCI、rpcd、dnsmasq、firewall4\u002Fnftables | WLOC 独立表；不触碰 Gateway 表和 UDP 500\u002F4500 |\n| 管理界面 | LuCI JavaScript | 自动\u002F手动切换、证书、状态和日志；敏感字段脱敏 |\n| 构建发布 | 固定摘要的 OpenWrt SDK \u002F Docker 镜像 | locked\u002Foffline 编译、SHA-256、架构标签不可伪装为 `all` |\n\n## 支持范围与验证状态\n\n“可安装”不等于“完成真实 iPhone\u002FWi‑Fi Calling 验证”。下表把证据等级分开列出：\n\n| 平台 | 架构 | 包管理器 | 当前证据 | 状态 |\n|---|---:|---|---|---|\n| Redmi AX6S · ImmortalWrt 24.10.6 | MediaTek MT7622 \u002F AArch64 | opkg | 官方 AArch64 OpenWrt 24.10.5 Docker 安装\u002F启动矩阵；另有实机 procd、LuCI、自动\u002F手动切换、证书和 iPhone WLOC 链路 | **Docker + 真机通过** |\n| OpenWrt 24.10.8 | x86_64 | opkg \u002F IPK | Docker 中启动 init\u002Fubus、安装集成包、启动服务、Socket 与 v1 状态检查 | **安装矩阵通过** |\n| iStoreOS 24.10.5 | x86_64 | opkg \u002F IPK | 同上 | **安装矩阵通过** |\n| OpenWrt 25.12.3 | x86_64 | apk \u002F APK v3 | 同上，使用原生 APK v3，非改名 IPK | **安装矩阵通过** |\n| 其他 OpenWrt \u002F ImmortalWrt 版本或 CPU | — | — | 尚无对应设备\u002FSDK证据 | **未验证** |\n\n运行时包包含 Rust ELF，**必须与路由器 CPU 架构一致**；LuCI 包才是 `all`\u002F`noarch`。x86_64 包由固定 SDK 构建，AX6S 使用单独的 AArch64 `cortex-a53` 交叉构建链；正式 Docker 矩阵会安装全部三个发布资产。Docker 验证的是安装与启动，不等同于 nftables、DNS、真实运营商或 iPhone 端到端测试。\n\n## 安装\n\n### 前置条件\n\n- sing-box、firewall4\u002Fnftables、LuCI 与 rpcd 可用。\n- 为测试 iPhone 建立固定 DHCP 地址，并在 Gateway 中绑定正确节点。\n- 已备份路由器配置；手机上的 WARP、Shadowrocket 或其他 VPN 在路由器 WLOC 测试期间保持关闭。\n- 只在专用测试设备上安装本项目 CA，并核对证书指纹。\n\n### 1. 选择正确的安装包\n\nRedmi AX6S 使用单一的架构专用集成包：\n\n- `wificalling-location-gateway_\u003C版本>_aarch64_cortex-a53.ipk`\n\n该包内含 Wi‑Fi Calling Gateway 1.7、WLOC 服务、控制工具和统一 LuCI，不依赖另行安装 `luci-app-wificalling-gateway` 或 `wloc-service`。重新安装或升级时，opkg 会保留 `\u002Fetc\u002Fconfig\u002Fwificalling-gateway` 与 `\u002Fetc\u002Fconfig\u002Fwloc-service`。\n\n正式版 1.0 对每个平台只提供一个完整集成包。包名统一为\n`wificalling-location-gateway`，内含 Wi‑Fi Calling Gateway 1.7、WLOC\n服务、控制工具和统一 LuCI；不再要求用户分别安装组件包。\n\n两种安装方式：\n\n**方式 A — 包源安装（推荐）**：添加签名包源后直接 `opkg install`：\n\n```sh\n# 导入源签名公钥（一次性）\nwget -O \u002Fetc\u002Fopkg\u002Fkeys\u002Ff7050198aa77cf15 \\\n  https:\u002F\u002Fraw.githubusercontent.com\u002Fsmthdagg\u002Fwificalling-location-gateway-feed\u002Fmain\u002Fwloc.pub\n# 添加源并安装\necho \"src\u002Fgz wloc https:\u002F\u002Fsmthdagg.github.io\u002Fwificalling-location-gateway-feed\" \\\n  >> \u002Fetc\u002Fopkg\u002Fcustomfeeds.conf\nopkg update && opkg install wificalling-location-gateway\n```\n\n**方式 B — 手动下载**：从\n[Releases](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Freleases)\n下载对应文件，并先校验同一发布目录中的 `SHA256SUMS`。\n\n两种方式的完整说明见\n[feed 仓库](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway-feed)（含\nOpenWrt 25.x 的 `.apk` 手动安装命令）。\n\n### 2. Redmi AX6S（单一集成 IPK）\n\n```sh\nopkg install \u002Ftmp\u002Fwificalling-location-gateway_\u003C版本>_aarch64_cortex-a53.ipk\n```\n\n不要先执行 `opkg remove`；直接安装即可恢复缺失组件并保留现有配置。安装后按“验证服务”一节检查两个服务。\n\n### 3. OpenWrt 24.10 \u002F iStoreOS 24.10（IPK）\n\n```sh\nopkg install \u002Ftmp\u002Fwificalling-location-gateway_1.2.0-r1_x86_64.ipk\n```\n\n### 4. OpenWrt 25.12（原生 APK v3）\n\n```sh\napk add --allow-untrusted \u002Ftmp\u002Fwificalling-location-gateway-1.2.0-r1.apk\n```\n\n`--allow-untrusted` 仅适用于当前未接入软件源签名的本地构建包。正式软件源发布应使用仓库签名，且不能把 IPK 重命名为 APK。\n\n### 5. 验证服务\n\n```sh\ntest -S \u002Fvar\u002Frun\u002Fwloc-service\u002Fcontrol.sock\n\u002Fusr\u002Fsbin\u002Fwloc-ctl status\n\u002Fetc\u002Finit.d\u002Fwificalling-gateway status\nlogread -e wloc-service\n```\n\n状态响应应包含 `\"api_version\":\"wloc.service\u002Fv1\"`。如果 LuCI 菜单未刷新，请清理浏览器缓存并重新登录，而不是反复安装不同架构的包。\n\n## 使用顺序\n\n请按以下顺序完成配置，避免把网络问题与位置问题混在一起：\n\n1. 在 **Wi‑Fi Calling Settings** 导入或添加节点，保存并应用。\n2. 在 **Device Policies** 添加测试 iPhone、固定 LAN IP、路由模式和绑定节点，再次保存并应用。\n3. 在 iPhone 开启 Wi‑Fi Calling，并在 **Wi‑Fi Calling Monitor & Log** 中观察 UDP 4500 `ASSURED`；最后必须以真实呼入\u002F呼出确认。\n4. 在 **WLOC Settings** 复制路由器生成的配置描述文件链接，用 iPhone Safari 下载并安装。\n5. 在 iPhone 的“设置 → 通用 → 关于本机 → 证书信任设置”中，为 `wloc-service root CA` 开启完全信任，并核对指纹。\n6. 开启 WLOC interception，选择 **Auto (follow node)** 或手动位置，保存并应用。\n7. 切换飞行模式\u002FWi‑Fi 或重新打开地图、天气应用以触发位置请求。\n8. 在 **WLOC Monitor & Log** 核对模式、国家、城市、时区、坐标、Geo 状态和更新时间。\n\n完整图文步骤请阅读：\n\n- [Wi‑Fi Calling + WLOC 中文完整使用教程](docs\u002FWIFICALLING_WLOC_TUTORIAL_ZH.md)\n- [Wi‑Fi Calling + WLOC Complete User Guide (English)](docs\u002FWIFICALLING_WLOC_TUTORIAL_EN.md)\n- [AX6S 部署与真机验证记录](docs\u002Fdeployment\u002FAX6S_DEPLOYMENT.md)\n\n## 从源码构建与验证\n\n### Rust 质量门禁\n\n```sh\n.\u002Fscripts\u002Fci\u002Fverify.sh\n```\n\n该入口执行格式、Clippy、单元\u002F集成测试、Rust 行覆盖率（最低 80%）、依赖审计、许可证策略、秘密扫描、发布体积和仓库契约检查。正式版 1.0 验证基线为 **69 个 Python 测试通过、Rust 行覆盖率 ≥80%、release 验证二进制约 0.97 MB**。\n\n### AX6S \u002F AArch64 交叉构建\n\n```sh\nOPENWRT_BIN_NAME=wloc-service \\\nOPENWRT_CROSS_CACHE_DIR=\u002Ftmp\u002Fwloc-rust-openwrt \\\n.\u002Fscripts\u002Fci\u002Fverify-rust-openwrt.sh\n```\n\n该流程固定 OpenWrt 24.10.8 `mediatek\u002Fmt7622` 工具链、Rust 版本和 SHA-256，并验证 AArch64 ELF、静态链接与体积。详见 [Rust OpenWrt 交叉构建说明](docs\u002Ftesting\u002FRUST_OPENWRT_CROSS_BUILD.md)。\n\n### x86_64 双格式打包\n\n```sh\n.\u002Fscripts\u002Fopenwrt\u002Fbuild-x86_64-runtime.sh \\\n  --out-dir \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\"\n\n.\u002Fscripts\u002Fopenwrt\u002Fbuild-release-packages.sh \\\n  --version 1.0.2 \\\n  --release 1 \\\n  --arch x86_64 \\\n  --service-bin \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\u002Fwloc-service\" \\\n  --ctl-bin \"$PWD\u002Fdist\u002Fruntime\u002Fx86_64\u002Fwloc-ctl\" \\\n  --gateway-ipk \u002Fabsolute\u002Fpath\u002Fluci-app-wificalling-gateway_1.7.3-1_all.ipk \\\n  --gateway-sha256 \u003Cverified-sha256> \\\n  --out-dir \"$PWD\u002Fdist\u002Fopenwrt-release\"\n```\n\n### 全部发布包的四环境 Docker 安装与启动矩阵\n\n```sh\n.\u002Fscripts\u002Fopenwrt\u002Fverify-docker-matrix.sh \\\n  --dist-dir \"$PWD\u002Fdist\u002Fv1.0.2\"\n```\n\n构建使用固定摘要的官方 OpenWrt SDK；依赖准备之后，产品编译采用 locked\u002Foffline、只读源码和禁网容器。完整边界和结果见 [OpenWrt 发布打包与 Docker 矩阵](docs\u002Ftesting\u002FOPENWRT_PACKAGE_DOCKER_MATRIX.md)。\n\n## 语言组成\n\n下面是 GitHub Linguist 在 2026-08-13 对当前主分支给出的代码字节快照。Python 主要用于可复现构建、fixture 治理和 CI；路由器产品运行时以 Rust 为主，Shell 负责 OpenWrt 生命周期与网络集成。\n\n```mermaid\npie showData\n    title GitHub Linguist language snapshot\n    \"Python · 59.03%\" : 63959\n    \"Rust · 22.40%\" : 24273\n    \"Shell · 18.56%\" : 20112\n```\n\n> 统计会随主分支更新而变化；LuCI JavaScript、文档和生成\u002F排除文件是否计入，以 GitHub Linguist 规则为准。项目的技术主语言不应只按仓库辅助工具的字节数判断。\n\n## 项目结构\n\n```text\nsrc\u002F                         Rust 服务、协议、TLS\u002FH2、出口与 Geo 模块\nopenwrt\u002F                     procd\u002FUCI、LuCI\u002Frpcd 与 OpenWrt 包定义\nscripts\u002Fopenwrt\u002F             交叉构建、双格式打包与 Docker 矩阵\nscripts\u002Fci\u002F                  覆盖率、安全、依赖和仓库质量门禁\ntests\u002F                       Rust、Python、JavaScript 与网络模型测试\nfixtures\u002F                    合成\u002F授权脱敏 fixture 契约与校验器\ndocs\u002F                        API、安全、部署、测试和双语用户教程\n.handoffs\u002F                   多 Agent 可复现接管记录\n```\n\n## 安全、隐私与回滚\n\n- CA 私钥只保存在路由器本地，权限为 0600；不得提交到 Git、支持包或日志。\n- 不提交节点密钥、Token、原始抓包、设备标识、精确用户位置或原始 WLOC 响应。\n- 仅允许指定测试设备和明确主机范围；普通 HTTPS、其他 LAN 设备及 UDP 500\u002F4500 不属于 WLOC 数据面。\n- 上游验证、ALPN、资源上限或 Geo 校验失败时不得以“看似成功”的默认位置继续运行。\n- 停用时先撤销 WLOC 重定向，再排空并停止服务；恢复前应确认独立 nftables 规则已消失。\n- 删除 iPhone 上的 `wloc-service root CA` 描述文件即可撤销设备信任；重新生成 CA 后必须重新核对并安装新指纹。\n\n漏洞请按 [SECURITY.md](SECURITY.md) 私下报告，不要在公开 Issue 中粘贴证书、IP、节点配置或设备信息。\n\n## 参与开发\n\n本仓库使用 GitHub Issue 作为唯一可分配工作单元，并以独立分支、路径租约、可复现 handoff 和异角色审查完成集成。提交前必须：\n\n1. 阅读 [AGENTS.md](AGENTS.md) 与对应 Issue 的 owned paths；\n2. 先写失败测试，再完成最小实现；\n3. 运行 `.\u002Fscripts\u002Fci\u002Fverify.sh`；\n4. 检查差异中没有秘密、私钥、设备数据和无关改动；\n5. 通过 Pull Request 合并，安全敏感变更不得由作者自审。\n\n详细协作方式见 [多 Agent 工作流](docs\u002FMULTI_AGENT_WORKFLOW.md)。\n\n## Star 增长\n\n[![Star History](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002Fsmthdagg\u002Fwificalling-location-gateway@star-chart\u002Fdocs\u002Fimages\u002Fstar-history.svg?v=2)](https:\u002F\u002Fgithub.com\u002Fsmthdagg\u002Fwificalling-location-gateway\u002Fstargazers)\n\n> 图表由 `star-history-chart` 工作流每日自动更新（也可在 Actions 页面手动运行）：用 GitHub 自动注入的 token 读取官方 star 时间线并本地生成 SVG，token 不写入仓库任何文件，也不经过第三方服务。main 受分支保护，图表发布在 `star-chart` 分支并经 jsDelivr CDN 嵌入；首次生成前的 star 历史（GitHub 未开放时间线访问的时期）不会出现在图中。\n\n如果这个项目对你的 OpenWrt \u002F Wi‑Fi Calling 实验有帮助，欢迎 Star、提交可复现的问题报告，或在 [LINUX.DO](https:\u002F\u002Flinux.do\u002F) 社区交流使用经验。请勿在公开内容中发布个人位置、证书或代理凭据。\n\n## 开源许可\n\n本项目采用 [MIT License](LICENSE)。第三方依赖及外部项目仍分别遵循其自身许可证；MIT 授权不改变 [clean-room 边界 ADR](docs\u002Fadr\u002F0001-license-boundary.md) 中对外部 AGPL 实现材料的隔离要求。\n\nWi‑Fi Calling Gateway 1.7 仍由独立仓库维护。本仓库不 vendor 它的源码；正式包构建只接受经过身份、版本和 SHA-256 校验的已发布 IPK，并在构建时组合为单一安装包。\n","这是一个为 OpenWrt\u002FImmortalWrt 路由器设计的 Wi-Fi Calling 地理位置网关轻量级实现，专注于 Apple 设备的 WLOC（Wi-Fi Location）协议响应处理。项目以 Rust 编写，提供独立、可审计的多代理工作流，支持自动（基于出口 IP 地理定位）和手动（指定经纬度）两种位置模式，具备证书生命周期管理、精准流量隔离（仅拦截 TCP 443 至 Apple WLOC 域名）、LuCI 图形化配置界面，并严格与现有 Wi-Fi Calling 网关（如 v1.7）的数据平面解耦。适用于家庭或实验室环境中对 Wi-Fi Calling 位置服务进行合规性测试、网络调试及策略验证。",2,"2026-08-16 02:30:03","CREATED_QUERY"]