usque · MASQUE / Connect-IP client

Cloudflare WARP for your OpenWrt router.

usque is a small Rust client for Cloudflare WARP built on MASQUE / Connect-IP (HTTP/3). This project packages it for OpenWrt 25.12 with a LuCI app, a procd service and a signed apk feed. It connects in TUN mode and — unlike the first release — leaves your routing alone until you opt in: each connection decides whether IPv4 traffic should use the tunnel, and existing IPv6 routing is respected.

  • OpenWrt 25.12 · apk
  • arm_cortex-a7_neon-vfpv4
  • Opt-in routing
  • Multiple connections
  • MIT

Stable release 0.2.1-r1 (usque) / 1.1.1-r1 (LuCI app): the v2 feature matrix — opt-in routing, IPv6, multiple connections, custom endpoints and LAN IPv6/NAT66 — is verified on a live OpenWrt 25.12.6 router.

Based on FireMasterK/usque-rs · tunnel and routing verified on a live ipq40xx (armv7) router with OpenWrt 25.12.6 · not affiliated with Cloudflare

What it is

Cloudflare WARP is Cloudflare's consumer VPN-ish service. usque speaks its MASQUE transport directly and bridges it to a Linux TUN interface. This port wires the client into OpenWrt's service manager, firewall and web UI — and separates two things that used to be bundled together: connecting the tunnel and routing traffic into it.

Safe by default: no IPv4 route takeover. Installing and starting usque creates the tunnel but leaves your IPv4 routing, ip rules, DNS and firewall forwarding alone. IPv4 traffic uses the tunnel only when you enable Default route for a connection or add your own routes. This is a deliberate change from v0.1, where every connection installed a default route immediately.

The one default routing change is IPv6: with ipv6_route=auto (default) a tunnel IPv6 default route is installed only when the router has no native IPv6 default route — that is how IPv4-only/CGNAT WANs get IPv6. Existing IPv6 routing is never replaced; set ipv6_route='off' (or ipv6='0') if you want no IPv6 routing changes at all.

MASQUE / Connect-IP

Uses Cloudflare's WARP endpoints over HTTP/3 (QUIC) with Connect-IP (RFC 9484) for the tunnel's addresses — no WireGuard required.

One-click registration

The LuCI app generates a WARP device identity per connection: accept the terms, click once, and a config file such as /etc/usque/warp0.json is created.

IPv4 routing is opt-in

Each connection has its own Default route switch, off by default. Enable it for a classic full tunnel, or leave it off and route selected prefixes yourself. IPv6 is handled separately by ipv6_route.

Multiple connections

/etc/config/usque holds one config usque section per tunnel, each with its own interface, config file, metrics and routing switches.

IPv6, even on IPv4-only WANs

The tunnel gets IPv6 by default; ipv6_route=auto adds an IPv6 default route only when the router does not have a native one. A separate LAN option can hand a ULA prefix to your devices via NAT66.

Cloudflare or custom endpoints

mode=cloudflare registers a WARP device for you. mode=custom imports a JSON config for another compatible MASQUE/Connect-IP endpoint.

Small and static

Cross-compiled for musl with LTO and size optimisations, stripped and packaged as an apk: 1.40 MB download, about 2.9 MB installed. The LuCI app adds a 21 KB package (~55 KB installed).

TUN mode only

SOCKS5, HTTP proxy, port-forwarding and the HTTP/2 fallback transport were removed to keep the binary small. The router routes packets; nothing else to configure.

Two states: connected vs. routed

Default: connected, routing untouched

  1. LAN clients
  2. Router WAN
  3. The internet exactly as before

The tunnel interface exists and is up, but no IPv4 default route, IPv4 policy rule or DNS change is installed — nothing is IPv4-routed through it. Exception: on a WAN without native IPv6 the tunnel may already provide an IPv6 default route (ipv6_route=auto).

Opt-in: Default route enabled

  1. LAN clients
  2. Router firewall forward + masquerade
  3. warp0 TUN default route, chosen metric
  4. Cloudflare WARP MASQUE over UDP/443
  5. The internet

The hook also installs an endpoint bypass route via the WAN and a high-priority policy rule; both are removed when the tunnel stops.

Requirements

RequirementDetails
OpenWrt 25.12.x apk-based (apk-tools 3) with fw4. The packages are built against the 25.12.6 SDK; install on 25.12.x only.
Architecture arm_cortex-a7_neon-vfpv4 (target ipq40xx/generic). This is the only architecture in the feed right now — apk will refuse to install it on other targets.
TUN support The kmod-tun package (installed automatically as a dependency) and a kernel built with TUN support. Standard OpenWrt images have both.
Free space About 3 MB free in /overlay: the package build reports 2,950 KiB installed for usque + luci-app-usque. Check with df -h /overlay first — on a 3.3 MB-overlay router this is tight but fits.
Network Outbound UDP/443 to the WARP endpoints (MASQUE over QUIC/HTTP 3). This build is QUIC-only: it has no TCP/HTTP 2 fallback, so if your ISP or upstream network blocks UDP/443 the tunnel cannot connect. In that case use the Go implementation of usque, which supports HTTP/2, instead.
LuCI luci-base and a web browser for the guided setup. The daemon and the CLI work without LuCI if you prefer SSH.
LAN IPv6 (optional) Only if you enable lan_ipv6: the default OpenWrt image's odhcpd (RA/DHCPv6) and firewall/NAT66 support are used. Hands out a local ULA prefix to LAN devices and NATs it through the tunnel — not a globally routed prefix. Requires an enabled connection with IPv6 on and ipv6_route=auto or always.
Shell access SSH or LuCI terminal as root for the install commands.
Correct clock TLS needs a roughly correct system time. Routers usually sync via NTP after boot; check date if the tunnel refuses to start.
Measured footprint Static binary: 2,905,040 B (2.77 MiB), downloaded as a 1.40 MB apk. On the test router the daemon used ~6.7 MB resident memory while connected and roughly 1 % of one CPU core when idle. The tunnel runs in user space, so throughput is bounded by the router's CPU.

Before you start

Three quick checks tell you whether the packages will install and run:

# release + architecture (expect 25.12.x / arm_cortex-a7_neon-vfpv4)
grep -E '^(DISTRIB_RELEASE|DISTRIB_ARCH)=' /etc/openwrt_release

# free space in the overlay (want ~3 MB or more)
df -h /overlay

# TUN support (kmod-tun is installed as a dependency, but check the kernel module)
lsmod | grep tun

# optional: existing IPv6 default route (ipv6_route=auto leaves it alone)
ip -6 route show default

If lsmod prints nothing, the module is not loaded yet — installing the package pulls in kmod-tun. If your image has no TUN support at all, the tunnel cannot start; use a standard OpenWrt image.

Install

Run these on the router over SSH. The feed is a signed apk repository served from GitHub Pages. Installing the packages does not start a tunnel and does not change any routing — you configure and opt in afterwards.

Back up first. Once you enable routing for a connection, the tunnel adds routes and firewall state while it is up. A quick backup of /etc/config/network, /etc/config/firewall, /etc/config/usque and any /etc/usque/*.json identity is always worthwhile.

Quick install

Trust the feed key, add the repository, refresh the index and install the LuCI app (which pulls in the usque daemon and kmod-tun):

wget -O /etc/apk/keys/usque.pem https://fmu.github.io/openwrt-usque/feed/usque.pem
echo "https://fmu.github.io/openwrt-usque/feed/packages.adb" >> /etc/apk/repositories.d/customfeeds.list
apk update
apk add luci-app-usque

Step by step

  1. Install the feed's public key

    apk verifies every repository index against the keys installed in /etc/apk/keys/. In apk v3 the key is matched by its public-key identity, so the filename is not significant; we use usque.pem so it stays recognisable and matches the commands on this page.

    wget -O /etc/apk/keys/usque.pem https://fmu.github.io/openwrt-usque/feed/usque.pem

    If wget is missing or fails with a TLS error, use uclient-fetch -O /etc/apk/keys/usque.pem <same URL> instead.

  2. Add the repository

    OpenWrt's apk keeps extra repositories in /etc/apk/repositories.d/*.list. Append the feed's index URL:

    echo "https://fmu.github.io/openwrt-usque/feed/packages.adb" >> /etc/apk/repositories.d/customfeeds.list

    Running this twice adds the line twice — harmless, but remove duplicates if you want a tidy file. The URL points directly at the packages.adb index (ndx repository type in apk v3); package files are then fetched from the same /feed/ directory, which is why the feed is flat.

  3. Refresh and install

    apk update
    apk add luci-app-usque

    The luci-app-usque package depends on usque and kmod-tun, so one command installs the daemon, the init script, the UCI configuration and the LuCI view. For a CLI-only setup use apk add usque.

    Already installed? apk add luci-app-usque only installs missing packages. With apk-tools 3 it does not upgrade an existing installation — apk just reports the package as “upgradable from …” and keeps the old version. Use the upgrade steps below instead.

    Check what you got — the two packages are versioned independently:

    apk info -v usque            # usque-0.2.1-r1
    apk info -v luci-app-usque   # luci-app-usque-1.1.1-r1
    sha256sum /usr/bin/usque
    # 413515df54fe2376dd2948f31ed548477604d6bb351f4909e70b8a4658ae5c9a
  4. Open LuCI

    Go to Services → usque and continue with First run. The direct URL is http://192.168.1.1/cgi-bin/luci/admin/services/usque (adjust the router IP).

Signature & trust

The feed index (packages.adb) is signed with an ECDSA P-256 key. The matching public key is what you downloaded to /etc/apk/keys/usque.pem and is also committed in the repository as keys/usque.pem. Verify the download if you want to be sure:

sha256sum /etc/apk/keys/usque.pem

The SHA-256 of the published key file (as served at https://fmu.github.io/openwrt-usque/feed/usque.pem and committed as keys/usque.pem in the repository) is:

f47c63b55e2b28f49d2ff1f751ea6401043cb30821620ad2ac9ad1c7561abe4f

If the two hashes match, the key you installed is the one the feed is signed with. If the feed is ever re-keyed, this page is updated with the new fingerprint. You can also verify an individual package file that you downloaded yourself with apk verify <file.apk>.

If the key is missing or does not match the index signature, apk refuses with an UNTRUSTED signature error. Fix the setup rather than bypassing it — but if you are stuck (for example, the key download was intercepted or the feed was rebuilt), you can install without verification as a last resort:

apk add --allow-untrusted luci-app-usque

--allow-untrusted disables signature checking for that transaction. Only use it with artifacts you obtained over a trusted connection, and remove the repository afterwards if you cannot restore proper verification.

Remove the repository again

To stop using the feed without touching the installed packages:

sed -i '\#fmu.github.io/openwrt-usque#d' /etc/apk/repositories.d/customfeeds.list
rm -f /etc/apk/keys/usque.pem
apk update

Then uninstall the packages if you no longer need them.

Upgrading an existing install

apk-tools 3 does not upgrade installed packages through apk add: with an older version already installed, apk update && apk add luci-app-usque leaves it in place and only mentions that it is “upgradable from …”. Upgrade in one of these ways:

# 1) upgrade just the two usque packages (recommended)
apk update
apk upgrade usque luci-app-usque

# 2) or upgrade every installed package
apk update
apk upgrade

# 3) or pin exact versions from the feed
apk policy usque luci-app-usque          # show versions available in the feed
apk add usque=<version> luci-app-usque=<version>
# e.g. apk add usque=0.2.1-r1 luci-app-usque=1.1.1-r1

The current feed provides usque 0.2.1-r1 and luci-app-usque 1.1.1-r1. A plain apk add luci-app-usque will only install it on a clean system; use one of the upgrade commands above on an existing install.

apk upgrade without package names upgrades all installed packages — convenient, but it can also update unrelated packages (and pull a new kernel on some systems). Naming the packages (apk upgrade usque luci-app-usque) or pinning versions with apk add <pkg>=<version> keeps the change targeted. Use apk policy to see the versions in the feed and apk info -v to check what is installed.

Coming from v0.1.x? The automatic route takeover is gone: after upgrading, check the Default route switch of each connection — it defaults to off — and enable it if you want the full-tunnel behaviour you had before. See Routing.

First run in LuCI

The LuCI app lists your connections, creates WARP identities, starts the service and shows live status. Routing stays off until you enable it.

  1. Open Services → usque

    In the LuCI menu under Services, pick usque (Cloudflare WARP). The page shows a Connections table — initially one connection named warp0 — with its enabled checkbox, interface, mode, IPv4/IPv6 routing status and per-connection actions (Start / Stop / Restart / Test / Logs / Remove). Below it are the selected connection's settings, the WARP account / configuration file section, Global settings and diagnostics. Each connection is an independent tunnel with its own interface and config file.

  2. Generate a WARP configuration for the connection

    For the default connection, the Account / configuration file section asks you to create an identity. Enter an optional device name (it shows up in your Cloudflare WARP device list), tick the box accepting Cloudflare's terms, and click Generate WARP configuration.

    Under the hood this runs, for that connection's config file:

    usque --config /etc/usque/warp0.json register --accept-tos --force -n "my-openwrt"

    The result is /etc/usque/warp0.json: an anonymous WARP device identity containing a private key and an access token. It is created with owner-only permissions — treat it like a password and never publish it. Ticking the box is what accepts Cloudflare's WARP terms; if you skip the UI and just start the service with auto_register=1, the init script registers a device for you and accepts the terms implicitly.

    Registration needs working internet. It happens over HTTPS to Cloudflare through your normal WAN before the tunnel exists, so the router must already be online (and its clock roughly correct) when you press the button.

  3. Enable and start

    Tick Enabled for the connection (stored as its enabled option), click Save & Apply, then press the connection's Start action. The state should switch to running within a few seconds, and the connection's interface (warp0) appears with its tunnel address.

    The init script only starts connections whose enabled option is set: pressing Start on a disabled connection tells you to enable it and Save & Apply first. The same applies to /etc/init.d/usque start. If no connection is enabled at all, Save & Apply stops and disables the service.

  4. Verify the tunnel

    With the service running, the connection shows its state, addresses and the last connection error (if any). The app's connection test runs a probe from the router:

    wget -qO- https://www.cloudflare.com/cdn-cgi/trace | grep -E '^(ip|loc|colo|warp)='

    With IPv4 routing still off, this reports warp=off — that is expected: the tunnel is up but no IPv4 traffic is routed into it yet. (On a WAN without native IPv6 the tunnel may already provide IPv6, because of ipv6_route=auto; see Routing.) The connection table labels this state IPv4: not routed; IPv4 route pending means the option was saved but the connection has not been restarted yet.

  5. Choose whether traffic uses the tunnel

    Open the connection's settings. The routing switches are off/unset by default:

    • Default route (off): send all IPv4 traffic through the tunnel — the classic full-tunnel mode. Default route metric sets the route metric (lower wins).
    • IPv6 route (auto): install an IPv6 default route only if the router has no native/WAN IPv6 default route yet. always installs it regardless; off never does.
    • Enable IPv6 on this tunnel (on): the tunnel's own IPv6 address.
    • Global settings → Provide IPv6 to LAN hosts (NAT66 through the tunnel) (off) and LAN IPv6 prefix: give LAN devices IPv6 via ULA + router advertisements, NAT66ed through the tunnel.

    Save & Apply (which restarts the connection), then re-run the check above. With Default route on it should now report warp=on and a Cloudflare egress IP. Details and manual alternatives are in Routing.

Registration is per connection and per device. Each generated identity counts as one device in your Cloudflare WARP account. Keep the JSON file if you want to reinstall or move the router without registering again.

Routing: what changes, and only when you ask

usque has two independent layers: establishing the tunnel (always) and sending traffic into it (opt-in). This section describes exactly what each switch changes on the network, so there are no surprises.

Defaults change no IPv4 routing. A fresh install installs no IPv4 default route, no ip rule for IPv4 and no DNS change. The tunnel interface is created, the firewall zone and forwardings are prepared and the daemon waits — IPv4 keeps routing exactly as before. The only default routing change is IPv6: when the router has no native IPv6 default route, ipv6_route=auto installs a tunnel IPv6 default route (plus its IPv6 policy rule) so IPv6 works on IPv4-only WANs. Stop or disable removes everything usque added.

Verified on the live test router (usque 0.2.1-r1 / luci-app-usque 1.1.1-r1): with the defaults, IPv4 egress stayed warp=off (no IPv4 default route touched) while IPv6 egress through the tunnel was warp=on, on an IPv4-only uplink.

Routing options

SettingDefaultWhat it changes while connected
default_route (per connection) 0 (off) When on: installs ip route replace default dev <iface> metric <metric> for IPv4. The existing WAN default route is kept, so a failing tunnel falls back to the WAN instead of taking the router offline. Removed when the connection stops.
metric (per connection) 0 Metric used for the tunnel default route(s). Lower wins; with two connections routing by default, give them different metrics to control priority.
ipv6 (per connection) 1 (on) Gives the tunnel its IPv6 address (with 0 the daemon runs with --no-tunnel-ipv6 and IPv6 routing is skipped for this connection). This is the tunnel's IPv6; reaching the endpoint over IPv6 is the separate endpoint_ipv6 option.
endpoint_ipv6 (per connection) 0 (off) Connect to the MASQUE endpoint over IPv6 (--ipv6) — only for native-v6 or v6-only networks. Not exposed in the LuCI form; set it with uci.
ipv6_route (per connection) auto auto: install an IPv6 default route via the tunnel only if the router has no native/WAN IPv6 default route (so IPv4-only/CGNAT WANs get IPv6). A default route installed by another usque connection does not count as native. always: install it regardless — this can override a native IPv6 uplink. off: never install one.
lan_ipv6 (global) 0 (off) Gives LAN clients IPv6 through the tunnel (ULA + router advertisements + NAT66), so they work on an IPv4-only WAN. It only takes effect when a connection with IPv6 enabled provides the IPv6 default route (ipv6_route=auto or always). The helper fills in missing settings only (network.globals.ula_prefix, LAN ip6assign, dhcp.lan.ra) and never replaces existing configuration. Local ULA only — devices are not globally reachable. Off by default; see LAN IPv6.
lan_ipv6_prefix (global) empty ULA prefix to use (/48 or /64) when lan_ipv6 is on. Empty = derive one from the LAN MAC address; set it if you already use a ULA range.
Firewall zone on service start When the service starts, a dedicated usque zone is created/updated for all enabled tunnel interfaces (input REJECT, output ACCEPT, forward REJECT) with masquerading (IPv4 + IPv6) and MSS/MTU fixing, plus forwarding into it from every internal zone that forwards to wan (always lan, and e.g. guest). Preparing the zone does not redirect traffic; the wan zone is never modified. The zone stays configured when the service is stopped and is removed on uninstall.
Endpoint bypass when routing is on A host route to the endpoint via the current WAN for the address family whose default route is being installed, so tunnel packets can reach the endpoint. Only needed — and only installed — when usque actually installs a route for that family.
Policy rule when routing is on ip rule priority 1000 from all lookup main, per address family whose default route is installed, so mwan3-style fwmark rules cannot divert traffic away from the main table. With defaults no rule is installed and mwan3 is untouched. Ownership is ref-counted across connections: the rule is removed when the last routing connection stops (and cleaned up from kernel state if the run-state directory was lost). A priority-1000 rule that already existed before usque started is left in place.
DNS follows routing No DNS setting is changed. LAN clients keep using the router's dnsmasq; its upstream queries follow whatever default route you chose. With defaults, DNS behaves exactly as before; with Default route on, resolution goes through the tunnel.
Stop / disable — Removes the tunnel route, endpoint bypass and policy rules for that connection. The WAN default route was saved once, shared by all routing connections, and is restored when the last of them stops. Your WAN routing and LAN access to the router keep working throughout.

How do I get traffic through the tunnel?

  1. Recommended: enable Default route

    Turn on Default route in the connection's settings (or set default_route='1' in UCI) and save. That is the classic full-tunnel mode: forwarded LAN traffic and router-originated traffic use the tunnel, with the WAN kept as fallback.

    uci set usque.warp0.default_route='1'
    uci commit usque
    /etc/init.d/usque restart

    The UCI example assumes the default connection is called warp0; adjust the section name if you renamed it.

  2. Route only selected prefixes yourself

    With Default route off, the tunnel device is a normal interface — add your own routes for the destinations you want tunneled. usque does not manage these, so re-add them after a restart (or script them in a hotplug file):

    ip route add 203.0.113.0/24 dev warp0
    ip route add 198.51.100.0/24 dev warp0
    ip route show | grep warp0

    This also makes the tunnel interface usable by other routing software (mwan3, VPN policy routing, custom nftables marks) without usque touching the main table.

  3. Use policy routing

    For per-client or per-protocol tunnelling, combine the tunnel device with your own ip rule/nftables marks (or mwan3). Keep in mind that if you later enable Default route, usque installs the priority-1000 rule that makes the main table authoritative while the tunnel is up — stop the service if you need your own policy rules to win.

  4. IPv6

    IPv6 needs no route to connect, but traffic needs one to be routed. With ipv6_route=auto (default) a tunnel IPv6 default route is installed only when the router has none. Use always to force it, or off to leave IPv6 routing untouched. To give LAN devices IPv6 on an IPv4-only WAN, see LAN IPv6.

LAN IPv6 (ULA + NAT66)

On an IPv4-only WAN (common with CGNAT, tethering or DS-Lite), LAN devices have no IPv6. With lan_ipv6=1 (global setting) usque gives the LAN a unique local address prefix, announces it via router advertisements and NAT66s it through the tunnel, so devices get working IPv6 to the internet. It only takes effect when a connection with IPv6 enabled provides the IPv6 default route (ipv6_route=auto or always).

Verified end-to-end. On the live test router (usque 0.2.1-r1 / luci-app-usque 1.1.1-r1) a Mac on the LAN received a ULA address from the announced prefix (fd12:…) and curl -6 through the router reached Cloudflare WARP (ip=<WARP_EGRESS_IP>, warp=on, colo=VIE); the revert was clean.

AspectDetails
Requiresodhcpd (default OpenWrt image) and an enabled connection with IPv6 on and ipv6_route=auto or always, so the tunnel provides the IPv6 default route. The prefix is local/ULA — devices are not globally reachable.
What it setsOnly missing settings are filled in: network.globals.ula_prefix (derived from the LAN MAC unless lan_ipv6_prefix is set), an IPv6 prefix on the LAN (ip6assign=60 if neither ip6assign nor ip6addr exists), and the RA/DHCPv6 server (dhcp.lan.ra=server, dhcpv6=server). Existing configuration is never replaced.
NAT66The usque firewall zone already has masq6=1, so LAN IPv6 is NATed through the tunnel. Inbound IPv6 connections do not work.
DisableSet lan_ipv6='0' and restart the service. This is intentionally non-destructive: the generated network/dhcp settings stay in place. Remove network.globals.ula_prefix, network.lan.ip6assign / ip6addr and dhcp.lan.ra manually to fully revert.

mwan3 and other policy routing

By default, usque does not install any policy rule, so mwan3 keeps working normally. Only when Default route is enabled — or an IPv6 route is installed — does the hook add ip rule priority 1000 from all lookup main (and its IPv6 counterpart), because otherwise mwan3's fwmark rules can silently send traffic to the WAN while the tunnel looks healthy. The rule is removed when the tunnel stops. If you need mwan3's per-WAN policies while the tunnel is up, stop the service or leave Default route off and route only selected prefixes yourself.

What WARP is not. WARP changes your apparent egress IP to Cloudflare and encrypts traffic between your router and Cloudflare's edge. It is not an anonymity tool, it does not hide your traffic from Cloudflare, and it is not a substitute for a commercial VPN. Cloudflare's own terms apply to the WARP service.

Multiple connections

Every tunnel is a separate config usque '<name>' section in /etc/config/usque, with its own interface, config file, mode and routing switches. The default connection is warp0.

One Cloudflare connection

The default setup: one WARP identity, one tunnel. Enable Default route for a full tunnel, or leave it off and route selected prefixes.

Two WARP identities

Separate config files (and separate Cloudflare devices). Give them different interface names, and if both route by default, different metric values.

Cloudflare + custom endpoint

Keep a Cloudflare connection for general traffic and add a mode=custom tunnel to another MASQUE/Connect-IP provider, routing only some prefixes through it.

Managing connections in LuCI

The connection table lists every connection with its mode, interface, IPv4/IPv6 routing state and per-connection actions (Start / Stop / Restart / Test / Logs / Remove). Selecting a connection shows its settings: mode, config file, interface name, MTU, endpoint port, the routing switches and (Cloudflare mode) the account card with generate / re-register / delete configuration. Each enabled connection runs as its own procd instance; starting the service starts all of them.

Removing a connection stops it and deletes its UCI section but keeps its config file on disk — delete the file separately if you also want to drop the WARP registration.

Connection example

A minimal /etc/config/usque with the default Cloudflare connection and a second tunnel. The second section shows the shape of an additional connection (here a second Cloudflare identity; for non-Cloudflare endpoints see custom mode):

config usque 'warp0'
	option enabled '1'
	option mode 'cloudflare'
	option config_file '/etc/usque/warp0.json'
	option interface 'warp0'
	option mtu '1400'
	option endpoint_port '443'
	option default_route '0'
	option metric '0'
	option ipv6 '1'
	option endpoint_ipv6 '0'
	option ipv6_route 'auto'
	option always_reconnect '1'
	option auto_register '1'
	option device_name ''
	option log_level 'info'
	option sni ''
	option authority ''

config usque 'backup'
	option enabled '0'
	option mode 'cloudflare'
	option config_file '/etc/usque/backup.json'
	option interface 'warp1'
	option default_route '1'
	option metric '10'

The second connection is disabled in this example (enabled '0') — enable it when you need it. The default config_file is /etc/usque/<section>.json; each connection needs a unique interface. Option names, defaults and the global LAN IPv6 settings are listed in Advanced.

Custom MASQUE / Connect-IP endpoints

Set mode='custom' to skip Cloudflare registration and use credentials you already have — in LuCI choose Custom endpoint as the connection's mode, then paste the JSON into the Configuration JSON box and press Import, or write the file yourself. There is no registration in this mode. Required fields: private_key, endpoint_v4, endpoint_pub_key and ipv4; optional: ipv6, endpoint_v6.

JSON fieldMeaning
private_keyYour EC P-256 private key, base64 DER — the same encoding usque uses for WARP configs. Required.
endpoint_v4, endpoint_v6IP address(es) of the MASQUE endpoint (UDP/443 by default). endpoint_v4 is required; endpoint_v6 is optional.
endpoint_pub_keyEndpoint's public key as a PEM block (the same format as a WARP config). Required.
ipv4, ipv6Addresses assigned to the tunnel interface by your provider. ipv4 is required; ipv6 is optional.
{
  "endpoint_v4": "203.0.113.10",
  "endpoint_v6": "",
  "endpoint_pub_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n",
  "private_key": "<base64 DER EC P-256 private key>",
  "ipv4": "172.16.0.2",
  "ipv6": "fd00::2"
}

Shape only — get the real values from your provider. Importing writes the file atomically with mode 600. Provider-specific TLS settings are separate connection options (not part of the JSON): sni (-s; LuCI TLS SNI (custom mode)), authority (--authority; LuCI HTTP authority (custom mode)) and protocol (--protocol; LuCI CONNECT protocol (custom mode) — pick connect-ip for an RFC 9484 server, leave empty for Cloudflare's cf-connect-ip). logread -e usque reports the handshake result.

The endpoint must speak a compatible MASQUE/Connect-IP service. Custom connections are not registered or re-registered by the init script, and auto_register does nothing for them.

Advanced configuration

Everything the LuCI app sets is plain UCI in /etc/config/usque, so it can be scripted, versioned or set over SSH.

UCI options

Each config usque '<name>' section is one connection. The section name is the connection name used by LuCI, the procd instance name and the state directory (/var/run/usque/<name>/); the packaged default is warp0. Options are read as usque.<name>.option.

OptionDefaultMeaning
enabled0Start this connection with the service/boot (each enabled connection runs as its own procd instance).
modecloudflarecloudflare registers/manages a WARP device; custom uses your own JSON credentials for another MASQUE/Connect-IP endpoint.
config_file/etc/usque/<section>.jsonIdentity/credentials file for this connection (mode 600). The default connection therefore uses /etc/usque/warp0.json.
interfacewarp0TUN interface name; must be unique across connections.
mtu1400TUN MTU. Lower it (e.g. 1380) for problematic mobile uplinks.
endpoint_port443UDP port used to reach the endpoint.
default_route0Install an IPv4 default route via the tunnel while connected (removed on stop). Off by default — see Routing.
metric0Metric for the tunnel default route(s); lower wins when several connections route by default.
ipv61Tunnel IPv6 address (with 0 the daemon runs with --no-tunnel-ipv6). Does not select the endpoint address family — that is endpoint_ipv6.
endpoint_ipv60Connect to the MASQUE endpoint over IPv6 (--ipv6); only for native-v6 or v6-only networks.
ipv6_routeautoauto installs an IPv6 default route only if the router has none; always installs it regardless; off never installs one.
always_reconnect1Reconnect automatically when the tunnel drops.
auto_register1Cloudflare mode only: register a WARP device if the config file is missing. Registration is non-interactive, so it accepts Cloudflare's WARP terms on your behalf — set it to 0 if you want to be asked.
device_nameemptyName used when registering a new device.
sniemptyTLS SNI for custom endpoints (passed as -s <sni>). Not needed for Cloudflare WARP.
authorityemptyCONNECT :authority presented to a custom MASQUE server (--authority; a bare host becomes https://<host>). LuCI: HTTP authority (custom mode).
protocolemptyCONNECT protocol token for custom endpoints (--protocol): empty keeps Cloudflare's cf-connect-ip, use connect-ip for RFC 9484 servers. LuCI: CONNECT protocol (custom mode).
log_levelinfoDaemon log level: error, warn, info, debug.

Global options

Two options live in the config settings 'settings' section and apply to the whole package rather than a single connection (UCI path usque.settings.*). In LuCI they are in Global settings.

OptionDefaultMeaning
lan_ipv60Give LAN clients IPv6 through the tunnel (ULA + router advertisements + NAT66; see LAN IPv6).
lan_ipv6_prefixemptyULA prefix to use (/48 or /64) when lan_ipv6 is on; empty = derived from the LAN MAC.

Example: turn on full-tunnel routing for the default connection, force IPv6 through the tunnel and give LAN clients IPv6:

uci set usque.warp0.default_route='1'
uci set usque.warp0.ipv6_route='always'
uci set usque.settings.lan_ipv6='1'
uci commit usque
/etc/init.d/usque restart

Config files and identities

Each connection points at its own JSON file (for the default connection: /etc/usque/warp0.json). In Cloudflare mode it holds the device's private_key and access_token; in custom mode it holds the fields listed under custom mode. Files are written with mode 600 (root only).

The default path is /etc/usque/<section>.json (so the default connection uses warp0.json). If a file ever ends up with the wrong mode, LuCI offers Fix permissions (chmod 600) for the selected connection.

  • Back one up off-device to keep the same WARP device across reinstalls: cp /etc/usque/warp0.json /tmp/ and copy it away.
  • Delete it to force a fresh registration: rm -f /etc/usque/warp0.json then restart (with auto_register=1) or press Re-register in LuCI.
  • Never share it — anyone with the file can use your WARP identity.

CLI

The daemon is also fully usable without LuCI. Registration is always per connection config file:

# register a new Cloudflare device for a specific connection
usque --config /etc/usque/warp0.json register --accept-tos --force -n "my-openwrt"

# run a tunnel in the foreground (what the init script does for each connection)
usque --config /etc/usque/warp0.json native-tun -n warp0 -m 1400 -P 443 --always-reconnect

# show help and version
usque --help
usque --version

Manual native-tun runs do not install routes by themselves — the packaged service adds the routing hook and applies the switches described above. Use the service for routed setups. The init script maps the UCI options to CLI flags: ipv6='0' → --no-tunnel-ipv6, endpoint_ipv6='1' → --ipv6, sni → -s, authority → --authority, protocol → --protocol.

Service commands and logs

/etc/init.d/usque start          # every enabled connection
/etc/init.d/usque start warp0    # one connection (section name)
/etc/init.d/usque stop warp0
/etc/init.d/usque restart warp0
/etc/init.d/usque stop
/etc/init.d/usque reload         # re-read UCI, restart the instances
/etc/init.d/usque status
/etc/init.d/usque enable         # start at boot
/etc/init.d/usque disable

logread -e usque                 # all connections (lines are prefixed with the section name)
logread -e usque -f              # follow live

If you edit UCI directly and only need the new options applied, run uci commit usque && /etc/init.d/usque restart. procd restarts a connection at most 5 times within an hour (10 s apart) — after that it stays stopped until you restart the service, so fix the underlying error first.

Status over ubus

The LuCI app is backed by an rpcd script, which is also handy for scripts and monitoring.

ubus call luci.usque status
ubus call luci.usque getConfig            '{"section":"warp0"}'   # public fields only
ubus call luci.usque test                 '{"section":"warp0"}'
ubus call luci.usque logs                 '{"section":"warp0"}'
ubus call luci.usque setInitAction        '{"action":"restart","section":"warp0"}'
ubus call luci.usque register             '{"section":"warp0","name":"my-openwrt","accept_tos":true}'
ubus call luci.usque registerStatus       '{"section":"warp0"}'
ubus call luci.usque importConfig         '{"section":"backup","json":"{...}"}'
ubus call luci.usque deleteConfig         '{"section":"backup"}'
ubus call luci.usque fixConfigPermissions '{"section":"warp0"}'

The private key and access token are never returned by getConfig; importConfig only works for connections in custom mode.

Troubleshooting

Start with logread -e usque | tail -n 40 — the daemon logs connection and routing decisions. The cases below cover most installs.

usque is running but nothing is routed — is it broken?

No — that is the default for IPv4. With default_route='0' the tunnel is connected but no IPv4 traffic is sent into it (IPv6 may already be routed on WANs without a native IPv6 default). To route everything, enable Default route for the connection (or uci set usque.warp0.default_route='1'; uci commit usque; /etc/init.d/usque restart), or add your own routes for selected prefixes. Check with:

ip route show | grep -E 'warp|default'
wget -qO- https://www.cloudflare.com/cdn-cgi/trace | grep -E '^(ip|warp)='
Traffic still exits via my ISP while routing is enabled (warp=off)

This is the policy-routing trap: mwan3 (or any fwmark/IP-rule based policy routing) can be evaluated before the main table and silently send traffic to the WAN, even though ip route get shows the tunnel. When routing is enabled, usque installs a priority-1000 rule to prevent it. Check that it is present:

ip rule show | grep '^1000:'
ip -6 rule show | grep '^1000:'

# if it is missing, add it for the current session:
ip rule add priority 1000 from all lookup main
ip -6 rule add priority 1000 from all lookup main   # only with IPv6 routing

logread -e usque should contain installed IPv4 policy rule: priority 1000 from all lookup main. On stop the rule is removed again — mwan3 keeps working normally when the tunnel is off.

IPv6 is connected but hosts still have no IPv6 route

With ipv6_route='auto' (default) usque only installs an IPv6 default route when the router has no native/WAN IPv6 default route. A leftover or provider-assigned default route therefore wins and is left untouched by design (a route installed by another usque connection does not count as native). The connection table shows this as “IPv6: auto (no route needed)”. Inspect and choose:

ip -6 route show default
uci set usque.warp0.ipv6_route='always'   # force the tunnel
uci commit usque && /etc/init.d/usque restart

# or disable IPv6 for this connection entirely:
uci set usque.warp0.ipv6='0'

For LAN devices on an IPv4-only WAN you additionally need lan_ipv6 (next entry) — a tunnel IPv6 route alone does not give clients addresses.

LAN devices got no IPv6 addresses

lan_ipv6 is off by default. It needs an enabled connection with IPv6 on and ipv6_route set to auto or always, so the tunnel provides the IPv6 default route; the helper then fills in missing LAN settings. Check all of it:

uci show usque.settings
uci show network.globals.ula_prefix
uci show network.lan | grep -E 'ip6assign|ip6addr'
uci show dhcp.lan | grep -E 'ra|dhcpv6'
ip -6 addr show br-lan
logread -e usque | tail -n 20

If you already run native IPv6 on the LAN, leave lan_ipv6 off — the helper should not compete with your existing RA/DHCPv6 setup. Disabling lan_ipv6 is non-destructive: the generated network/dhcp settings stay, remove them manually if you want to revert.

Only one of my connections is used, or routes flap between them

Only one IPv4 default route can win, and one IPv6 default route can win. If two connections both have Default route enabled, give them different metric values (lower wins), or enable routing on just one of them and route other prefixes manually. The hook ref-counts routing ownership across connections: the policy rule and the saved WAN default route are only removed when the last routing connection stops. Check the interfaces and routes:

uci show usque | grep -E 'default_route|metric|interface'
ip route show default
ip -6 route show default
A custom (non-Cloudflare) connection does not connect

Custom mode skips registration and uses what is in the JSON file (import it in the connection's Configuration JSON box, or write it yourself). Common causes: wrong or expired keys, the endpoint unreachable over UDP/443, or a missing sni when the provider requires TLS SNI (uci set usque.<section>.sni='example.com', passed as -s), an HTTP authority (uci set usque.<section>.authority='example.com', passed as --authority) or a different CONNECT protocol token (uci set usque.<section>.protocol='connect-ip' for an RFC 9484 server; empty means Cloudflare's cf-connect-ip). Check logread -e usque for handshake errors, verify the required fields against custom mode, and confirm the same credentials work with another client. Remember: auto_register has no effect in custom mode.

The LuCI menu entry “usque” is missing after installation

The package is installed but LuCI's cached menu index still needs a refresh:

rm -f /tmp/luci-indexcache*
/etc/init.d/rpcd restart
/etc/init.d/uhttpd restart

Then hard-refresh the browser (Ctrl/Cmd+Shift+R), log out and back in. Confirm the package is really present with apk info -e luci-app-usque and that /usr/share/luci/menu.d/luci-app-usque.json exists.

The service will not start, or a connection never comes up

Work through these in order:

  1. logread -e usque | tail -n 40 — read the last error.
  2. Is the connection enabled? uci show usque | grep -E 'enabled|interface'. The init script only starts connections with enabled=1, and if none is enabled the service is stopped and disabled.
  3. Is there an identity (Cloudflare mode)? ls -l /etc/usque/warp0.json. If it is missing, either enable auto_register or register from LuCI / the CLI.
  4. Is the clock right? date. A router whose time is far off cannot complete TLS. Let NTP sync (/etc/init.d/sysntpd restart) or set the time in LuCI.
  5. Is TUN available? lsmod | grep tun and ls -l /dev/net/tun. Install the module with apk add kmod-tun and reboot.
  6. If the log says could not resolve route to endpoint, the hook deliberately left your default routes untouched (safe). Check that the WAN has a default route and a reachable gateway, then restart the service.
  7. Is UDP/443 blocked? The tunnel needs UDP/443 to Cloudflare's WARP endpoints (the config file lists them, e.g. 162.159.198.2). Some tethering, hotel, corporate or ISP setups block it. This build is trimmed to QUIC and has no TCP fallback — if UDP is blocked it cannot connect, and you need a WARP client with HTTP/2 support such as the Go implementation of usque.
“UNTRUSTED signature” when running apk update / apk add

The index signature does not match any key in /etc/apk/keys/. Re-download the key, check the fingerprint, and run apk update again. With apk v3 the key is identified by its public-key identity (the filename does not have to be usque.pem, but the key material must match the one the feed was signed with). If the feed was re-keyed, remove the stale key file first. The --allow-untrusted fallback is a last resort only.

I ran apk update && apk add but the version stayed the same

With apk-tools 3, apk add only installs missing packages; it does not upgrade an installed one (apk prints “upgradable from …” instead). Upgrade with apk upgrade usque luci-app-usque, apk upgrade for everything, or pin versions: apk add usque=<version> luci-app-usque=<version>. Then check:

apk info -v usque
apk info -v luci-app-usque
apk policy usque luci-app-usque
No internet after stopping or uninstalling the tunnel

What usque added is removed on stop. If a crash left something behind, restore normal routing:

ip rule show
ip route
# remove leftovers if any:
ip rule del priority 1000
ip route del default dev warp0
fw4 reload
/etc/init.d/network restart

Also check ip -6 route if IPv6 routing was enabled. Rebooting always brings you back to a clean state.

apk fails with “No space left on device”

The binary is a few megabytes and some routers have a tiny overlay. Check the space and free some up:

df -h /overlay
du -sh /usr/bin/usque
apk cache clean

Remove packages you do not use (apk del <name>), clear the LuCI cache (rm -f /tmp/luci-indexcache*), or move the overlay to a USB stick with extroot if your device supports it. A 16 MB-flash router will usually struggle to fit a multi-megabyte binary; a device with more flash is a better fit.

Device limit, or re-registering creates a new device every time

Every Generate WARP configuration registers a new device in your Cloudflare WARP account, and the free plan only allows a limited number of linked devices. This applies per connection: each Cloudflare connection with its own identity is another device. Reuse an existing JSON file instead of re-registering, or remove unused devices in the Cloudflare WARP dashboard / Zero Trust device list. If registration fails with a device-limit error, delete the old device first and try again.

LAN clients are not using the tunnel

The tunnel only affects traffic that follows the router's default route or your own routes. Check that the connection has Default route enabled (or that your static policy routes apply), that the usque firewall zone and forwardings exist (fw4 print | grep -A4 usque; lan and any other internal zone that forwards to wan are wired up automatically), and that clients use the router as their gateway and DNS server. A client with a static public DNS or its own VPN bypasses the router's tunnel. Also check the priority-1000 ip rule if you run mwan3.

Slow or unstable connection

Try, one change at a time: lower the MTU to 1380 (mobile uplinks), check CPU load — encryption at a few hundred Mbit/s is a lot for a small router — and make sure UDP/443 is not throttled or blocked by your ISP. logread -e usque shows reconnect attempts.

Does it break SSH access to the router?

No. Routing changes only affect outbound/forwarded traffic; inbound SSH and LuCI on the LAN continue to work. With defaults, routing is not touched at all. If you manage the router from outside the LAN, keep a reliable out-of-band path before enabling Default route.

FAQ

Does installing usque change my routing?

No IPv4 routing is changed: installing and starting usque creates the tunnel but leaves your IPv4 default route, ip rules, DNS and firewall forwarding unchanged. IPv4 traffic only enters the tunnel after you enable Default route for a connection or add your own routes. IPv6 is the exception: with ipv6_route=auto a tunnel IPv6 default route is installed only if the router has no native IPv6 default route, so IPv6 works on IPv4-only WANs. Set ipv6_route='off' if you want no IPv6 routing changes either.

Why did the first release take over my routes?

v0.1.x always installed default dev <iface> metric 0 on connect. That was convenient for a full tunnel but surprising if you did not expect it. v2 makes routing an explicit per-connection choice, keeps the WAN default route as a fallback, and removes everything it added on stop.

Do I need a Cloudflare account?

No. Registration creates an anonymous free WARP device identity; usque talks to the public WARP service. No Cloudflare login is involved.

How do I route only some traffic through the tunnel?

Leave Default route off and add your own routes for the prefixes you want tunneled (ip route add <prefix> dev warp0), or use policy routing (mwan3, nftables marks, ip rule). See How do I get traffic through the tunnel?. This replaces the old "split tunnelling is not supported" limitation. For IPv6, ipv6_route='off' keeps usque out of the v6 routing table entirely.

Can I use more than one tunnel at a time?

Yes. Add another config usque section (or use the LuCI connection list) with its own interface name and config file. If you want both to route by default, give them different metric values — only one default route can win per address family. See Multiple connections.

Can I use a non-Cloudflare MASQUE/Connect-IP endpoint?

Yes, with mode='custom': provide the endpoint addresses, endpoint public key, your private key and the assigned tunnel addresses in the connection's JSON config. There is no registration in this mode. See custom endpoints.

Is WARP+ or Zero Trust supported?

The LuCI app and the Cloudflare mode focus on the free consumer WARP service. No license-key or organisation/Zero Trust fields are exposed. Custom mode is for other compatible MASQUE/Connect-IP endpoints.

Will it survive a reboot / apk upgrade?

Yes. Connections with enabled=1 start at boot, and identity files persist. Feed updates show up as normal package upgrades (apk upgrade) and keep your configuration.

Which routers are supported?

Right now the feed only contains arm_cortex-a7_neon-vfpv4 packages (ipq40xx/generic) built against OpenWrt 25.12.6. That is also the architecture the full v2 feature matrix was verified on (0.2.1-r1 / 1.1.1-r1, live OpenWrt 25.12.6 router). Other targets would need their own build — the workflow is in the repository if you want to add one.

Does the router still use my local DNS server (Pi-hole, AdGuard) on the LAN?

Yes — LAN clients can query whichever DNS server you advertise via DHCP. Queries from the router itself, and upstream queries from dnsmasq, follow the routing you chose. A separate LAN DNS appliance keeps answering local clients directly, but its own upstream traffic is only tunneled if it is routed through the router.

Where do I report bugs?

For this packaging (LuCI app, init script, apk feed) use the GitHub issue tracker. Bugs in the tunnel client itself belong upstream at FireMasterK/usque-rs.

Uninstall & cleanup

Stopping the service restores normal routing. Removing the packages cleans up everything except your saved configuration and identities.

/etc/init.d/usque stop
/etc/init.d/usque disable
apk del luci-app-usque usque
WhatAfter uninstall
Binary, init script, hook, helpersRemoved.
LuCI views, menu, ACL, rpcd backendRemoved.
Routes, policy rules, run stateRemoved on stop (and by a fallback in the init script). Transient state lives in /var/run/usque/ and disappears on reboot.
Firewall zone / forwardingRemoved by the package's prerm cleanup (which calls /usr/libexec/usque/firewall teardown). If you removed the package files by hand or a stale zone remains, check uci show firewall | grep -i usque, delete the matching sections with uci delete firewall.<section>, uci commit firewall and fw4 reload.
/etc/config/usqueKept (conffile). Delete it manually for a completely clean system: rm -f /etc/config/usque.
/etc/usque/*.jsonKept, because they are your device identities (one per connection). Delete the ones you will not reuse: rm -f /etc/usque/warp0.json.
Feed entry & keyStill configured. Remove them with the repository removal commands.

Remove old identities before re-registering elsewhere. If you move to a new router, keep the JSON files and reuse them — that avoids burning another device slot in your WARP account.

Source, builds & licences

Everything on this page is produced by GitHub Actions from the public repository — binary, apk packages, signed feed and this site.

Current release

usque 0.2.1-r1 · luci-app-usque 1.1.1-r1 for OpenWrt 25.12 (arm_cortex-a7_neon-vfpv4) — the two packages are versioned independently. Binary: 2,905,040 B (2.77 MiB), sha256 413515df…58ae5c9a. Installed size 2,950 KiB. Built by CI run 40373166952 from commit 5139af6. Confirm with apk info -v usque and apk info -v luci-app-usque.

Licence

MIT, same as upstream. The full text is in LICENSE.md (upstream copy: src/LICENSE.md).

Repository layout

PathContents
src/Vendored usque-rs at commit f2a073c (provenance in UPSTREAM.md), patched for OpenWrt: TUN only, ring crypto, size optimisations.
openwrt/usque/OpenWrt package: binary, procd init script, UCI defaults, routing/firewall connect + disconnect hooks, LAN IPv6 helpers.
openwrt/luci-app-usque/LuCI JS views (connection list, registration, routing switches, diagnostics), menu entry, ACL and the rpcd backend.
build/Reproducible armv7 cross-compile script used by CI, plus the patch notes (build/PATCHES.md).
scripts/Feed assembly and signing.
.github/workflows/CI: static armv7 build (build.yml), SDK package build + feed signing (package.yml), site + feed deployment (publish.yml).
keys/usque.pemPublic key used to verify the apk feed.
tests/Live-router verification scripts and results — see tests/RESULTS.md for the router model, test matrix and measured values.
docs/This static site (plain HTML/CSS, no build step).
LICENSE.md, README.mdMIT licence text and the project overview.

Build pipeline

  1. Build the binary — cross-compilation to armv7-unknown-linux-musleabihf with a static musl toolchain, LTO and opt-level="z", then stripped. See the workflows and build/build-usque.sh.
  2. Package & sign — the OpenWrt 25.12.6 SDK builds usque and luci-app-usque; scripts/ assembles the feed and signs packages.adb with an ECDSA P-256 key (package.yml).
  3. Publish — publish.yml copies docs/ to the site root, puts the public key and the signed feed into /feed/, and deploys the artifact to GitHub Pages.

Verification: the 0.2.1-r1 / 1.1.1-r1 packages were exercised on a live OpenWrt 25.12.6 ipq40xx router — signed-feed install, service lifecycle, opt-in IPv4 routing, IPv6 (auto and forced), multiple connections, custom MASQUE/Connect-IP mode and LAN IPv6/NAT66. Scripts, evidence and the check matrix are in tests/RESULTS.md.

Changes compared to upstream

The vendored client is trimmed and adapted to keep the binary small and make it manageable by procd/LuCI: TUN mode only (SOCKS5, HTTP proxy and port-forwarding code removed), QUIC-only transport (the HTTP/2 fallback is removed to save space), no BoringSSL (QUIC via ring, which supports 32-bit ARM), non-interactive register --accept-tos --force, and a size-optimised release profile. Multi-connection management, opt-in routing, IPv6 route selection and LAN IPv6/NAT66 are implemented in this repository's OpenWrt init script, routing hook and LuCI app rather than in the tunnel client. Details are recorded in build/PATCHES.md and UPSTREAM.md.

Credits & licence

  • Tunnel client: FireMasterK/usque-rs, MIT licensed (upstream text kept at src/LICENSE.md).
  • Packaging, init scripts, LuCI app and this site: MIT, same repository — see LICENSE.md.
  • Cloudflare WARP, WARP and the Cloudflare logo are trademarks of Cloudflare, Inc.

Not affiliated with Cloudflare or OpenWrt. This is an independent, community-style packaging of an open-source client. Cloudflare's WARP terms of service apply to your use of the WARP service — accept them knowingly and use the project at your own risk. No warranty is provided.

Privacy

This site has no analytics, cookies or external resources — it is plain HTML, CSS and a tiny inline copy-to-clipboard script. GitHub Pages serves it and may log request metadata as described in GitHub's privacy statement. The packages themselves contain no telemetry; the tunnel's traffic goes to Cloudflare by design, and custom endpoints receive their traffic instead.