How the qURL™ CLI behaves, tested
qURL CLI tests on October 1 and 18, 2026: streaming, session admission, Vite and Next.js. The follow-up used an existing resource.
Updated October 18, 2026. Licensed CC BY 4.0.
October 18 follow-up: CLI 3.5.0
The follow-up checked WebSockets, Server-Sent Events, chunked responses, a streaming chat sample, Vite 8.3.4 and Next.js 16.4.2 through qURL links. It used an existing local resource; it did not verify publishing a new resource. The recipient was a headless browser on the publishing machine, sharing its public IP address. No second device or network was tested.
Download the October 18 results. One run on one network, with the recipient browser on the publishing machine. Published for transparency, not as a latency benchmark.
| Check | Result | What happened |
|---|---|---|
| qurl share <CRID> on the existing resource | pass | JSON output reported expires_in_seconds 86400 and single_use true. |
| Where the recipient lands | observed | The browser went from qurl.link to https://r_<11 characters>.qurl.site/. |
| WebSocket upgrade through the link (probe-server.js) | pass | 3 of 3 echo round trips completed over wss://. |
| Server-Sent Events through the link (probe-server.js) | pass | Six events sent one second apart arrived 993 to 1,002 ms apart; none were batched. |
| Chunked streaming response through the link (probe-server.js) | pass | Five 1 KiB chunks sent one second apart arrived about one second apart. |
| Open the same link a second time | pass | The second open showed "Access link invalid": the link may have expired, been revoked, or already been used. |
| What the local app receives | observed | Host and X-Forwarded-Host are c-<connector id>.qurl.site; requests also carry X-Forwarded-For, -Port, -Proto, -Server, X-Real-IP and X-Request-Id; browser POSTs send Origin https://r_<id>.qurl.site. No request carried a cookie. |
| Streamed chat reply (public/data/qurl-streaming-chat/server.js, fetch reading text/event-stream) | pass | 49 words sent 150 ms apart arrived 152 ms apart at the median, at most 204 ms, never two within 20 ms. Pressing Stop after 5 words aborted the request in the browser, and the local server logged the connection closing after 6 words sent. |
| Vite 8.3.4 dev server (vite --host 127.0.0.1) through the link | fail without config, pass with it | Without configuration the recipient saw "Blocked request. This host ("c-<connector id>.qurl.site") is not allowed." With server.allowedHosts ['.qurl.site'] the page loaded, the hot reload WebSocket connected over wss://r_<id>.qurl.site, and a source edit was applied in the remote browser as a hot update, without a page reload. |
| Next.js 16.4.2 dev server (next dev -H 127.0.0.1, Turbopack) through the link | fail without config, pass with it | Without configuration next dev logged "Blocked cross-origin request to Next.js dev resource /_next/hmr" and edits never reached the remote browser. With allowedDevOrigins ['*.qurl.site'] hot reload worked, but a Server Action failed with "Invalid Server Actions request" (x-forwarded-host c-<id>.qurl.site does not match origin r_<id>.qurl.site). Adding experimental.serverActions.allowedOrigins ['*.qurl.site'] made the Server Action return 200, and a source edit was applied without a page reload. |
October 1 original run: what we ran
| CLI | qurl 3.2.0 (darwin/arm64), checksum-verified release binary |
|---|---|
| Account | none (fresh device identity in an isolated state directory) |
| Mode | qurl publish --foreground (no background service installed) |
| Operating system | macOS on Apple silicon |
| Browser | headless Chromium driven by agent-browser |
| Node.js | 20.18.3 |
| Probe app | probe-server.js in this folder, listening on 127.0.0.1 |
Results
| Check | Result | What happened |
|---|---|---|
| Publish a local app with no account | pass | qurl publish printed a CRID with no prompts, sign-in, or API key. |
| qurl share <CRID> with no flags | pass | JSON output reported expires_in_seconds 86400 (24 hours) and single_use true. |
| Where the recipient lands | observed | The browser went from qurl.link to https://r_<11 characters>.qurl.site/ with no warning or sign-up page. |
| WebSocket upgrade through the link | pass | The upgrade reached the local app and 3 of 3 echo round trips completed over wss://. |
| Server-Sent Events through the link | pass | Six events sent one second apart arrived one second apart; none were batched. |
| Chunked streaming response through the link | pass | Five 1 KiB chunks sent one second apart arrived one second apart. |
| Open the same link a second time | pass | The second open showed "Access link invalid" and stated the link may already have been used. |
| How an opened session is authorized | observed | No request carried a cookie. After the link was opened, a cookie-less curl from the same public IP address received the app. This matches the API reference: redeeming a link grants network access to the IP address that redeemed it. |
| Headers the local app receives | observed | Host and X-Forwarded-Host are c-<connector id>.qurl.site; X-Forwarded-Proto is http; browser requests send Origin https://r_<id>.qurl.site. |
| Reach the app after qurl delete | pass | The r_ host stopped answering after the resource was deleted. |
| Resource expiry reported by qurl list | observed | A resource created 2026-10-01 reported expires_at 2126-09-07, about 100 years out. |
| qurl publish http://localhost:5173 against a default Vite 6.4.3 dev server | fail | Vite listened only on [::1]; qurl dialled 127.0.0.1 and reported that the local app was not accepting TCP connections. Publishing http://[::1]:5173 worked. |
| Open a link to the Vite dev server | fail without config, pass with it | Without configuration Vite answered "Blocked request. This host is not allowed." With server.allowedHosts set to ['.qurl.site'] the page loaded, the HMR socket connected, and an edit to src/main.js reached the remote browser. |
Streaming timings
One run on one network. Published for transparency, not as a latency benchmark. Times are milliseconds after the page started the request, for the same probe app reached directly on localhost and through a qURL link.
| Stream | Localhost | Through qURL |
|---|---|---|
| Server-Sent Events | 1005, 2005, 3006, 4007, 5009, 6008 | 1176, 2175, 3171, 4174, 5171, 6182 |
| Chunked response | 1005, 2005, 3006, 4006, 5006 | 1165, 2175, 3172, 4174, 5170 |
What this means when you share an app
- Dev servers keep hot reload, because WebSockets and Server-Sent Events pass through.
- Dev servers with a host check need to allow qurl.site; see the dev server guide.
- Publish the address your app actually listens on: localhost is dialled as 127.0.0.1, so an IPv6-only app needs http://[::1]:PORT.
- A single-use link opens one session for one network address. Keep your app’s own login for anything sensitive.
Frequently Asked Questions
Does qURL support WebSockets and Server-Sent Events?
Yes. In this test a WebSocket upgrade reached the local app and completed three echo round trips, and six Server-Sent Events sent a second apart arrived a second apart, without batching.
Is a qURL link really single-use?
Links from qurl share are; the CLI has no option to change that. With no flags, qurl share reported single_use true, and opening the same link a second time showed “Access link invalid”.
Does opening a link set a cookie?
A cookie is not required. No request to the app carried one, and the session is admitted by network address: after the link was opened, a cookie-less request from the same public IP reached the app. Other devices behind that IP can reach the app until the session ends.
Cite this
LayerV (2026). How the qURL CLI behaves, tested. https://layerv.ai/research/qurl-behaviour (updated 2026-10-18). CC BY 4.0.