On this page
Wrabbit Documentation
Wrabbit is a free proxy client for iPhone and Mac built on the Xray core. It provides no servers of its own: you connect your own configurations and subscriptions. This page explains how to use the client and how providers can prepare a subscription for Wrabbit.
What Wrabbit is
Wrabbit is a client, not a VPN service. It has no servers of its own and sells no access. You add your own vless://, trojan://, ss:// links or a subscription URL — keys and configs stay only on your device.
- Two network cores: Xray-core for VLESS/Trojan/Shadowsocks and native Hysteria2 core for QUIC.
- Platforms: iPhone / iPad (iOS 16 and later) and Mac (macOS 13 and later) — one app, the same configs and subscriptions.
- Flexible IP-list routing and automatic server selection.
- Compatible with panel subscriptions (Remnawave and compatible) — no account, no sign-up.
wrab- and are additive: other clients (Happ, v2rayNG, Streisand, etc.) simply ignore unknown fields — the same subscription works for everyone.Quick start
- Install Wrabbit from the App Store: iPhone / iPad (iOS 16 or later) or Mac (macOS 13 or later).
- Tap “+” and add a configuration: paste a link from the clipboard, scan a QR code, or paste a subscription URL. The format is detected automatically.
- Pick a server (or “Auto” when the group has ≥2 servers) and tap “Connect”. On the first connection iOS or macOS will ask you to allow the VPN profile.
macOS
- The same client in the Mac App Store (Mac Catalyst): import, subscriptions, auto mode and routing work the same as on iPhone.
- A menu bar icon: quick connect/disconnect, status, open the window, settings and quit — without bringing the app to the foreground.
- QR codes are scanned with the Mac camera (on first use the system asks for permission; the prompt is localized to the app language).
If a subscription stops serving servers (expired or out of traffic), the card shows a banner and a “Renew access” button — access info comes from the subscription response, see “Subscriptions for providers”.
Importing configs and subscriptions
Supported formats
| Format | Support |
|---|---|
vless:// (single link) | Yes |
trojan:// | Yes |
ss:// (SIP002 and legacy base64) | Yes |
hysteria2:// / hy2:// | Yes — Hysteria 2 |
Subscription URL http(s):// | Yes |
| Base64 subscription blob | Yes — decoded automatically |
| Multi-line list of links | Yes |
Xray JSON (Remnawave /json, Happ) | Yes — array or single object; VLESS, Trojan, Shadowsocks, Hysteria2 |
vmess:// | Not supported |
| Clash / sing-box | Not supported |
| File import | Not supported (clipboard, QR, text) |
How to add
- Paste from the clipboard or into the import text field.
- Scan a QR code with the camera.
- Manual config editor (VLESS / Trojan / Shadowsocks / Hysteria2).
The client tells them apart: text starting with {/[ → Xray JSON; a single http(s):// link with no line breaks and no vless:// in the text → subscription; vless://, trojan://, ss://, hysteria2://, and hy2:// → standalone servers or bulk parsing.
Xray JSON (Remnawave)
Paste a Remnawave <sub_url>/json response (an array of configs with remarks) or a single config object — the client adds one server per entry with a supported proxy outbound. For VLESS / Trojan / Shadowsocks the tunnel starts from the entry’s native xray-json (panel outbounds, balancers and dialerProxy / fragment chains are kept); the client substitutes its SOCKS inbound and, when a Happ routing profile is present, may overlay that profile’s domain/DNS rules. Hysteria2 from xray-json is mapped into server fields and started by the separate Hy2 core — not via native xray-json.
- Supported outbounds in xray-json: VLESS (REALITY TCP/gRPC, xHTTP and WebSocket behind a CDN), Trojan, Shadowsocks and Hysteria2 — not only
vless://links. - Connect (VLESS/Trojan/SS) uses the native JSON; UI ping and auto-selection use preferred-outbound fields (or a native-outbound probe) so they do not measure a decoy. Hy2 uses a separate core.
- If a server was already added via a share link with the same parameters and the same name (dedupe key), no duplicate is created — the native config from the JSON is attached/updated on the existing server.
- For CDN (xHTTP) native configs the client may inject TLS ClientHello fragmentation on top of the panel config (per-server
fragment=or the global toggle) when the template has nofragmentoutbound yet. Fragmentation is not applied on REALITY.
Subscription updates
- The request uses the
WrabbitUser-Agent and bypasses cache (Cache-Control: no-cache), 20-second timeout. - A successful response fully replaces the subscription's server list; a manual selection is preserved if the same server remains.
- Network errors and 4xx/5xx codes leave the old list intact — an “Update failed” toast is shown.
- Auto-update runs once a day with a global toggle in settings. A subscription update-interval header is not honored at the moment.
Protocols and transports
| Protocol | Import | Transports |
|---|---|---|
| VLESS | vless://, xray-json | REALITY (gRPC, TCP), xHTTP/CDN (TLS), WebSocket (TLS), TCP without TLS (security=none) |
| Trojan | trojan://, xray-json | TCP/TLS, WebSocket/TLS, gRPC/TLS, TCP without TLS |
| Shadowsocks | ss://, xray-json | TCP (no SIP003 plugins); in xray-json also stream ws/grpc and TCP+TLS |
| Hysteria2 | hysteria2://, hy2://, xray-json | QUIC/UDP + TLS, Salamander obfs, port hopping |
| VMess | — | Not supported |
VLESS+REALITY share links require pbk and sni (optionally sid, flow, fp, servicename). xHTTP/CDN share links need path and sni/host (type alias: splithttp). Typical flow: none or xtls-rprx-vision (value is not strictly validated). Plain TCP VLESS (security=none / empty security, type=tcp|raw) is supported. Connections from xray-json use the panel’s native JSON (no second-pass transport filter).
hysteria:// / xray version=1), vmess://, Shadowsocks with SIP003 (?plugin=). VLESS share links are rejected for: WebSocket with security=none (or anything other than tls/empty), REALITY over WebSocket, type=tcp|raw + security=tls, type=grpc + security=tls (non-REALITY). In xray-json, VLESS tcp|raw+tls and grpc+tls are skipped; VLESS network=ws is imported for any security (including none/reality) and started from the native config. Trojan share type=ws does not reject security=none (mapped as WS+TLS).Routing
By default all traffic goes through the tunnel. Local/private networks are excluded automatically. Additionally, selected addresses can be sent directly, bypassing the tunnel (IP split-tunnel).
Routing lists
- Format — IPv4 CIDR (a single IP is treated as
/32). Separators: newline, comma, space or;. - Lists are added by text or by link (plain text file) and refreshed from that link.
- Each rule set is enabled with its own toggle. A bundled “RU zone” CIDR preset is no longer seeded: RU bypass comes from the subscription
routingprofile (geoip:/geosite:) plus a short hot-CDN Network Extension bypass.
Domain routing from subscription (Happ standard)
A provider can deliver routing rules with the subscription via the routing header (the same format as Happ: happ://routing/onadd/<base64-json>). Wrabbit applies DirectSites/ProxySites (domains and geosite:), BlockSites (explicit domains only), DirectIp/ProxyIp (CIDR and geoip:), BlockIp (explicit CIDRs only) when geo files are present. Domain rules work via connection sniffing. Manage it in Settings → Routing.
geosite:*, geoip:*) are supported: the app downloads geoip.dat / geosite.dat from the Happ profile URLs (Geoipurl / Geositeurl) into the App Group, and the Network Extension points xray at that directory. Tags in direct/proxy rules resolve when the files are present. If URLs are missing or download has not finished yet, matching geo tags are temporarily stripped (the tunnel still starts); after download the client reconnects an active tunnel so the NE picks up the .dat files. The Happ profile’s DomainStrategy is ignored: Network Extension always uses domainStrategy=AsIs. ext: / ext-domain: tags are unsupported. Block rules do not take geo tags (explicit domains/CIDRs only). Per-app split tunneling is not used in the client.Auto mode and server selection
The “Auto” row appears when a group has ≥2 servers (or always under wrab-mode: auto-only). On a cold start the client pings the group's servers through a proxy (an ephemeral SOCKS proxy on Xray or Hysteria2, check URL). Hedged selection may finish early only on the first live direct (.ok); a fast wl alone does not abort the run while direct candidates remain. Result: fastest live direct, otherwise wl. A repeat start from a fresh ranking cache (up to ~12 h) may connect without a new ping.
You assign roles: regular (direct) and allow-list reserve (wl). While on wl, the client periodically probes direct and returns to it when it recovers stably. On iPhone and Mac, with several servers, the tunnel can restart and switch between them on connectivity drops.
Wrabbit additionally fetches the same subscription in xray-json format (<url>/json) and uses the panel's native configs for VLESS/Trojan/SS hosts: the template's routing/DNS, and for multi-edge hosts the native balancer pool (Wrabbit DomainStrategy is always AsIs). If /json is unavailable or the subscription is third-party, the client builds configs from share links (vless:// / trojan:// / ss:// / hysteria2://) as before. No extra setup is needed on the provider side.
“via Proxy” ping and check URL
- Check URL:
https://cp.cloudflare.com/generate_204— success = HTTP 2xx (same as Happ “via Proxy”). - Connect uses native xray-json; UI ping and auto-selection use preferred-outbound fields / a native-outbound probe (not the first outbound in the array).
dialerProxy/ fragment chains from the native JSON are honored for ping too — a server that only works through fragment is not falsely rejected.
Subscriptions for providers
Wrabbit reads subscription metadata primarily from HTTP response headers. A # body-line fallback exists only for wrab-mode, hide-settings, and happ://routing/… (e.g. #wrab-mode: auto-only). The header always wins over the body line. All other keys in the table are header-only. If the main URL returns xray-json, body # lines are not parsed.
brand/hide/routing). Without a valid wrab-license token the client does not apply brand/routing, and hide is forced off. See the “Provider licensing” section for details.| Key | Source | Effect | Access |
|---|---|---|---|
wrab-mode | Header or #wrab-mode: | auto-only — hides the server list and pings; the picker shows a single row with the provider name (Profile-Title) and the subtitle “We'll automatically pick the fastest server for you”; selection is pinned to auto mode. | Free |
Profile-Title | Header | Subscription title (also the auto-only row title). Supports base64:<...>. | Free |
Subscription-Userinfo | Header | Traffic and expiry: upload=…; download=…; total=…; expire=…. | Free |
hide-settings | Header or #hide-settings: | 1/true/yes/on — hides technical server fields, copy, QR, share and editing. | License · hide |
Announce | Header | Announcement in the subscription card. Supports base64:<...>. | License · brand |
Support-Url | Header | Right-hand “Support”/“Telegram” button in the subscription card (next to “Website”). A t.me/telegram link gets the Telegram label and icon. http/https only. On an inactive subscription it is secondary; the primary “Renew access” prefers Profile-Web-Page-Url. | License · brand |
Profile-Web-Page-Url | Header | Left-hand “Website” button in the subscription card (provider site / dashboard). http/https only. On an inactive subscription this is the preferred URL for the primary “Renew access” button. | License · brand |
wrab-logo | Header | Logo URL in the subscription header (http/https). | License · brand |
routing | Header or happ://routing/… line | Site/IP routing rules (Happ standard): happ://routing/onadd/<base64-json>. Direct/Proxy take domains/CIDRs and geosite:/geoip: when .dat files from Geoipurl/Geositeurl are downloaded; BlockSites/BlockIp do not take geo tags. routing-enable: 0 forces the profile off. ext: is unsupported. | License · routing |
wrab-license | Header | Signed Wrabbit license token — unlocks premium features (brand/hide/routing) for subscriptions on this domain. Without a valid token they stay off. A #wrab-license: line in the body is not supported. See “Provider licensing”. | — |
Server zones (direct / wl) and the ~ marker
The client splits servers into two logical zones. The zone does not depend on transport — you set it with a name marker or a parameter on vless://.
direct— regular fast server (default).wl(whitelist) — reserve for allow-list networks; auto fails over to it when direct stops carrying data, and returns to direct when it recovers.- Explicit override only on
vless://: querywrab-zone=directorwrab-zone=wl(wins over the name). Synonyms:wl/whitelist/white/cdn/бс/bsanddirect/normal/default. Ontrojan:///ss:///hysteria2://and in xray-json the parameter is not read — zone comes from the name only. - From the name (remark /
#fragment): a separate wordWL,whitelist,white list,white zone,LTEorБС(case-insensitive) →wl; otherwise →direct.БСmatches remarks like[БС-4]. The wordCDNalone is not a zone marker. - A
~anywhere in the name — visible for manual selection and UI ping, but excluded from auto selection and NE failover. If every node in the group has~, the exclusion is ignored. Underauto-onlythe server list is hidden, so~only affects the auto pool (the user cannot pick the node manually).
| Node name (remark) | Zone / auto |
|---|---|
Germany 1 | direct, in auto |
All carriers WL | wl (word WL) |
LTE Megafon | wl (word LTE) |
🇳🇴 Norway [БС-4] | wl (word БС) |
NL CDN | direct — CDN is not a zone marker |
~ Lab QUIC | manual + UI ping only, not in auto |
Reserve ~ | manual only |
tag field (e.g. in Remnawave) does not appear in vless:// and does not control the zone. Only the name (#fragment / remark) and wrab-zone on vless:// do.Subscription response examples
Normal list + zones via wrab-zone (UI: servers visible, “Auto” row present). CDN in the name is just a label; the zone comes from wrab-zone=wl.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Profile-Title: base64:V3JhYg==
Subscription-Userinfo: upload=0; download=0; total=0; expire=1788000000
vless://uuid@nl1.example.com:443?type=grpc&security=reality&pbk=KEY&sni=cloudflare.com&sid=ab&wrab-zone=direct#NL-1
vless://uuid@cdn.example.com:443?type=xhttp&security=tls&sni=edge.example.com&path=/xh&host=edge.example.com&wrab-zone=wl#NL-1 WL
vless://uuid@lab.example.com:443?type=tcp&security=reality&pbk=KEY&sni=www.microsoft.com&sid=cd#~ Lab QUICauto-only: in Wrabbit the user sees the provider card and a single picker row titled with Profile-Title (not the word “Auto”) plus the auto-pick subtitle. Servers and pings are hidden; auto still prefers direct, then wl.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Profile-Title: Wrab
Subscription-Userinfo: upload=0; download=0; total=0; expire=1788000000
wrab-mode: auto-only
vless://uuid@nl1.example.com:443?type=grpc&security=reality&pbk=KEY&sni=cloudflare.com&sid=ab&wrab-zone=direct#NL-1
vless://uuid@cdn.example.com:443?type=xhttp&security=tls&sni=edge.example.com&path=/xh&host=edge.example.com&wrab-zone=wl#NL-1 WLThe same auto-only as a body line (when panel headers are unavailable). A header, if present, wins.
#wrab-mode: auto-only
#hide-settings: 1
vless://uuid@nl1.example.com:443?type=grpc&security=reality&pbk=KEY&sni=cloudflare.com&sid=ab#NL-1TLS ClientHello fragmentation for CDN (xHTTP+TLS only, not REALITY): fragment=1 or the full form fragment=tlshello,100-200,10-20. Other clients ignore the unknown key.
vless://uuid@cdn.example.com:443?type=xhttp&security=tls&sni=edge.example.com&path=/xh&host=edge.example.com&wrab-zone=wl&fragment=1#NL-1 WLExpired or disabled subscription
When a subscription is no longer active, the panel returns HTTP 200 with metadata but no servers. Wrabbit treats this as a “no access right now” signal (not an error): servers are removed, a banner and a “Renew access” button appear (preferring Profile-Web-Page-Url, else Support-Url), and an active tunnel on the vanished server is disconnected. The state clears on the next update once servers return.
Provider licensing (premium integration)
Wrabbit is a neutral free client: we take no payment from users and there are no paid features in the app. Some integration capabilities are premium and available to providers under a license (B2B, outside the app). This is the client's monetization: the provider pays for extended integration, the end user never does.
What is free and what needs a license
| Feature | What it unlocks | Headers |
|---|---|---|
| Baseline (free) | Importing servers and subscriptions, auto mode and server selection, server roles, title and traffic/expiry. Works for everyone, always. | Profile-Title, Subscription-Userinfo, wrab-mode, wrab-zone |
brand | Subscription branding: header logo, announcement, support and website/dashboard buttons. | wrab-logo, Announce, Support-Url, Profile-Web-Page-Url |
hide | Hiding server data in the UI: address/SNI/UUID/transport, copy, QR, share and editing (a lock is shown). | hide-settings |
routing | Delivering a domain routing profile with the subscription (Happ standard). | routing |
How it works
A license is a signed token bound to your subscription domain and an expiry. You put it in the wrab-license HTTP response header. A #wrab-license: line in the body is not supported. The client verifies the signature offline with an embedded key (Ed25519) — it never contacts our servers for this. If the token is valid, not expired, and its domain matches your subscription host, the matching premium features turn on. No token or an invalid one — those features are simply off (hide is forced off), and the rest of the subscription works as usual.
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Profile-Title: base64:V3JhYg==
Subscription-Userinfo: upload=0; download=0; total=0; expire=1788000000
wrab-logo: https://cdn.example.com/logo.png
Announce: base64:SGVsbG8h
Support-Url: https://t.me/example_support
hide-settings: 1
routing: happ://routing/onadd/<base64-json>
wrab-license: v1.<base64url(payload)>.<base64url(sig)>
vless://uuid@nl1.example.com:443?type=grpc&security=reality&pbk=KEY&sni=cloudflare.com#NL-1Token format
Token: v1.<base64url(payload)>.<base64url(Ed25519 signature)>. The payload is compact JSON: dom (subscription domain), feat (feature array — brand/hide/routing), exp (Unix expiry), kid (key id). The signature covers the string v1.<base64url(payload)>. Only we issue tokens: a signature cannot be forged with just the public key embedded in the client.
How to get a license
There is no self-serve dashboard yet — we issue licenses manually. Contact us (see Support): send your subscription domain(s) and the feature set you need, and we will issue a token for an agreed term. Renewal and revocation are on our side.
exp premium features turn off automatically, so renew ahead of time. Verification is offline, so there is no instant revocation — a revoked token stops working at its exp.Remnawave integration
Everything is configured with the panel's native tools, no forks. Labels are delivered three ways:
- Globally per client — Subscription Response Rules: a rule with
user-agent CONTAINS wrabbitand a header, e.g.wrab-mode: auto-only. Other clients are unaffected. - Per-user / per-group — External Squads: the
responseHeadersfield, e.g.{ "wrab-mode": "auto-only" }, assigned to selected users. - Server role — via a remark marker (e.g. a
WLorLTEsuffix) orwrab-zoneon thevless://host-address (not on trojan/ss/hy2).
Example External Squad responseHeaders (or customResponseHeaders):
{
"wrab-mode": "auto-only",
"wrab-logo": "https://cdn.example.com/logo.png",
"wrab-license": "v1.<base64url(payload)>.<base64url(sig)>"
}Branding in Remnawave: Announce ← happAnnounce, Support-Url ← supportLink (both in subscription-settings); Profile-Web-Page-Url ← the built-in isProfileWebpageUrlEnabled toggle (URL from SUB_PUBLIC_DOMAIN) and/or customResponseHeaders / an External Squad's responseHeaders; wrab-logo ← customResponseHeaders or an External Squad's responseHeaders only.
Domain routing: the routing header comes from Subscription Settings → Happ Routing (happRouting). Build the profile at routing.happ.su and deliver it as a single line — the same profile is applied by both Happ and Wrabbit (including geosite:/geoip: when Geoipurl/Geositeurl are set; Wrabbit DomainStrategy is always AsIs).
Native Xray configs: on subscription import and refresh the client additionally fetches <sub_url>/json and stores the full per-entry config (routing, DNS, balancers, burstObservatory) for every server. The connection is brought up from it as-is — the panel controls the behavior of all hosts exactly as it does for Happ, including standalone REALITY servers, Trojan/SS outbounds and multi-edge balancing. If a subscription has no /json endpoint, the client silently falls back to building configs from the links, as before.
Preferred outbound and decoy (for providers)
An entry often has several outbounds (working path + decoy). Connect always uses the native JSON (the panel's balancer/routing). UI ping and auto-selection look at the preferred outbound — otherwise ping may measure the decoy and show red while the tunnel is live.
Preferred-outbound selection order: routing.balancers[].fallbackTag → tag proxy-wl* / *-wl-* (Liberty) → non-decoy when the pool has *decoy* tags → pointer from routing.rules (outboundTag / balancerTag) → balancer.selector pool → transport cluster (when mixed, TLS-stream over Reality) → tag proxy → first proxy outbound.
- Recommended: set
routing.balancers[].fallbackTagto the working outbound — the most reliable signal for UI ping. - Decoy: put decoy in the
tag(e.g.proxy-decoy-…) or use a different transport than the working path — so the client does not mix them up when picking ping fields. dialerProxy/ fragment chains in native JSON are supported for ping as well (parity with connect).
Announce/Support-Url/Profile-Web-Page-Url/wrab-logo), hiding (hide-settings) and routing (routing) are premium features and, in new Wrabbit versions, only take effect with a valid wrab-license token. The token is delivered the same way as other headers: customResponseHeaders, an External Squad's responseHeaders, or Subscription Response Rules ({ "wrab-license": "v1.…" }). See the “Provider licensing” section for how to obtain one.Privacy and device
Wrabbit keeps no traffic logs and shares no configurations with third parties. On every subscription request (and the extra <sub_url>/json fetch) the client sends a device identifier and OS info in the x-hwid, x-device-os, x-ver-os headers; x-device-model is sent when the model can be determined — needed for device accounting in the panel (HWID device limit). There is no setting to disable HWID.
- Platforms: iPhone / iPad (iOS 16 and later) and Mac (macOS 13 and later). The tunnel uses Packet Tunnel / Network Extension on both.
- On Mac — a menu bar widget for quick VPN control without opening the window.
- No in-app analytics, trackers, or crash SDK; only system Apple crash reports may be sent if the user enabled them in the OS.
- Localization: Russian and English (plus a System option) — switch in Settings. The camera permission string is localized (ru/en) by the system language; the system VPN dialog follows the OS language (not an in-app language override).
Extra features
- On-demand auto-connect: the system brings the tunnel up and restores it whenever a network is available. A strict kill-switch is not used.
- Interface language (system / Russian / English) and once-a-day subscription auto-update — in Settings.
- macOS: menu bar widget — connect/disconnect, status, open the window and settings.
- Session traffic stats and server ping via proxy.
- QR import and export, copy and share links.
- Local config editing (unless hidden by the provider).
- TLS ClientHello fragmentation for CDN (xHTTP): a global toggle and the
fragmentquery onvless://(1ortlshello,100-200,10-20). - Tunnel journal and Xray logs with export for diagnostics.
Compatibility checklist
- Take the subscription link from the panel and test it in another client (e.g. Happ) —
wrab-*fields must not break parsing. - Open the subscription in Wrabbit without
wrab-mode— a server list; an “Auto” row when the group has ≥2 servers. - Add
wrab-mode: auto-only— server list and pings hidden; a single row with the provider name (Profile-Title) and the auto-pick subtitle (not the word “Auto”). - Roles: auto prefers
direct, falls back towl; hedge early-exit only on a live direct. A~in the name — manual selection and UI ping, not auto-selection. - Compare the “via Proxy” ping in Wrabbit and Happ — the order of magnitude should match (
https://cp.cloudflare.com/generate_204, HTTP 2xx). - Licensing: without
wrab-license, branding/hiding/routing are off (this is expected). With a valid token for your domain they turn on. If the token's domain does not match the subscription host, or the token is expired, the features stay off.
Provider smoke tests (xray-json)
<sub_url>/jsonreturns entries: after import, servers appear and connect brings up the tunnel from native JSON (panel routing/DNS/balancer).- Multi-outbound with decoy: set
fallbackTagto the working path — UI ping is green on a live WL path, not on decoy REALITY. - Decoy is marked in
tagor uses a different transport than the working path — ping and connect stay aligned. - Entry with
dialerProxy/ fragment: both “via Proxy” ping and connect succeed (not only connect). - Trojan or Shadowsocks in xray-json (not only
vless://): import and connect succeed; set their zone with a name marker (notwrab-zone). direct+wlpair: auto starts on direct and can fail over to wl; a node with~is skipped by auto.