vrok straight from your machine

How to use vrok

Free and open source. vrok is not a file-hosting site. It is a local server plus a tunnel: one command publishes a file, a folder, or a running HTTP app through a URL that is meant to die.

What it is

Your laptop sits behind NAT. Nobody on the internet can open a connection to it. vrok starts a small HTTP server on loopback, opens an outbound tunnel, and prints a public URL. Visitors hit that URL; bytes are read from your disk — or proxied to localhost — while they wait.

That covers three different jobs with the same command:

  • A file — video with seeking, a PDF, an installer, anything.
  • A folder — browsable listing, or a built site / Playwright report served as the real thing.
  • A running server — vrok localhost:3000 reverse-proxies it, WebSockets included, so hot reload still works for the other person.

Nothing is uploaded. Closing the terminal, hitting the TTL, or revoking the share is what stops the URL. If the recipient needs the file after you shut the lid, this is the wrong tool — see the comparison.

Updating

vrok never checks for updates in the background. When you want the newest release, ask for it:

vrok update

It finds the newest release on GitHub, downloads the build for your platform, checks it against the published checksum, makes sure it runs, and only then replaces itself. If Homebrew, Scoop or go install put vrok on your machine, it prints that tool's command instead, so the package manager stays in charge. Add --check to only see whether there is a newer release.

Newest release: see releases · what changed

Install a specific version

If a new release breaks something for you, go back to the one that worked. Pick the version from the changelog:

vrok update --version v0.6.0

Plain vrok update returns you to the newest release later. Releases older than 0.6.0 do not have vrok update, so install those with the install script, which takes the version from VROK_VERSION:

macOS · Linux
curl -fsSL https://raw.githubusercontent.com/AliJabbar034/vrok/main/install.sh | VROK_VERSION=v0.5.2 sh
Windows
$env:VROK_VERSION='v0.5.2'; irm https://raw.githubusercontent.com/AliJabbar034/vrok/main/install.ps1 | iex

The same variable pins a version in CI, so every run uses the release you tested with. Homebrew and Scoop only install the newest release. From source:

go install github.com/AliJabbar034/vrok/cmd/vrok@v0.6.0

Without vrok update

Older versions answer unknown command "update". Update once the way you installed, below. After that, vrok update works.

1. Stop running shares

Press Ctrl+C in any terminal running vrok. On Windows this is required: a running vrok.exe cannot be replaced. On macOS and Linux an open share keeps running the old version until you start it again.

2. Run the update for your install method

Install script macOS · Linux
curl -fsSL https://raw.githubusercontent.com/AliJabbar034/vrok/main/install.sh | sh
PowerShell script Windows
irm https://raw.githubusercontent.com/AliJabbar034/vrok/main/install.ps1 | iex
Homebrew macOS · Linux
brew update && brew upgrade --cask AliJabbar034/tap/vrok
Scoop Windows
scoop update; scoop update vrok
Go any platform
go install github.com/AliJabbar034/vrok/cmd/vrok@latest

Not sure how you installed it?

Ask your shell where vrok lives: which vrok on macOS and Linux, (Get-Command vrok).Source in PowerShell. Then match the path:

Path contains Installed with
/opt/homebrew/, linuxbrew Homebrew
/usr/local/bin/, ~/.local/bin/ Install script. On an Intel Mac, Homebrew also uses /usr/local/bin: if ls -l $(which vrok) points into Caskroom, it is Homebrew.
\AppData\Local\Programs\vrok\ PowerShell script
\scoop\shims\ Scoop
/go/bin/ Go

3. Check it

vrok --version

It prints vrok version X.Y.Z (…), where X.Y.Z is the newest release above without the v. A build from go install prints dev instead; that is expected, and it is still the latest code.

Still showing the old version?

  • Two copies on your PATH. vrok doctor lists them. The first one found wins. List them with which -a vrok or Get-Command vrok -All, then delete the old one or update it with its own method.
  • Your shell remembers the old path. Run hash -r, or open a new terminal. On Windows always open a new terminal after installing.
  • Homebrew says it is up to date. Its tap list is stale: brew update first, then upgrade again.
  • “Access is denied” on Windows. vrok is still running somewhere. Stop it, or check Task Manager for vrok.exe.
  • Something else? Open an issue with the output of vrok --version and the path from above.

Commands

vrok ./cut-03.mp4                 # one file
vrok ./playwright-report          # a whole directory
vrok report.pdf shot.png a.mp4    # several files, one index
vrok localhost:3000               # a local HTTP server
vrok share ./x                    # same as vrok ./x

A file

Recognised types get a preview (player, image, highlighted source). Everything else gets a download card with the right Content-Type. There is no allow-list: a 4 GB disk image, an .xlsx, or a file with no extension all work.

A folder

Visitors get a listing. If the folder contains index.html, that file is served at the root — which is why vrok ./dist looks like the real site. Add ?list=1 to any directory URL to force the listing.

Every listing has a Download all (.zip) button, and any subfolder can be zipped on its own. The zip is built while it is sent, so nothing is written to disk first. One zip counts as one download against --downloads.

A localhost app

vrok does not replace your framework. It reverse-proxies whatever is already listening. Range requests and WebSocket upgrades pass through on the default Cloudflare route, so Vite / Next hot reload reaches the person you sent the link to.

WebSocket upgrades are not carried through --tunnel relay yet. If you use your own relay for live frontend work, visitors must refresh.

Flags

Flag What it does
-i Three questions first: who can open it, how long it lasts, and the download limit. Then prints the equivalent flags.
--ttl 30m How long the share lives. 45s, 30m, 2h, 1d, 1w. Default: until you stop it.
--downloads 5 Close after five downloads. Seeking inside a video does not spend one.
--password Prompt for a visitor password. Also reads VROK_PASSWORD. Stored as Argon2id, never written to disk.
--qr Print a QR code next to the URL.
--local LAN only. No public tunnel. The banner says your local network.
--tunnel local This machine only. A 127.0.0.1 URL cannot be sent to anyone.
--tunnel <name> auto (default), cloudflare, or relay.
--name client-report Display name on the share and in vrok list.
--port 8080 Pick the local listen port instead of a free one.
-v Request and tunnel diagnostics.

While a share is running, c copies the URL, q prints a QR code, p adds a password, e changes the expiry, 1 makes it one-time, and x stops. Each change keeps the same URL. Scripts and CI never see the hotkey bar.

Large files

While someone downloads, one line under the banner shows how it is going. When they finish, a summary stays behind:

↓ 1.2 GB / 4.0 GB · 30% · 38 MB/s · 1m left
  ↓ Sent 4.0 GB in 1m 48s · 38 MB/s average
  • Resumable. A dropped download picks up where it stopped, and resuming does not spend one of your --downloads.
  • Your computer stays awake. While a transfer is running, vrok keeps the machine from going to sleep on its own, and lets it sleep again 30 seconds after the last one ends. Closing the lid still sleeps.
  • Whole folders in one go. See A folder above.
  • A fingerprint to check against. The file page shows the file’s SHA-256, worked out in the background after the share starts. The recipient compares it with shasum -a 256 file on macOS or Linux, or certutil -hashfile file SHA256 on Windows.

Speed is set by your upload bandwidth and the tunnel, not by vrok: every byte comes from your machine. Watch a transfer on the homepage.

Managing shares

vrok list               # every share running on this machine
vrok list --json        # the same, for scripts
vrok revoke a82kd9      # kill one URL now
vrok stop --all         # stop every vrok process
vrok config set ttl 30m # store a default
vrok update             # install the newest release
vrok doctor             # check this machine for common problems

There is no database. Each sharing process reports itself over a socket in your state directory. When the process exits, it disappears from the list.

When something breaks

Run the doctor first. It checks the things that most often stop vrok from working, changes nothing, and finishes in a few seconds:

vrok doctor
✓ Install      /usr/local/bin/vrok (the install script)
! PATH         2 copies of vrok; the first one runs:
               /usr/local/bin/vrok
               /Users/ana/go/bin/vrok
               Delete the ones you do not use.
✓ Config       defaults (no config file)
✓ cloudflared  fetched by vrok
! Port 8080    in use; `vrok --local` needs it free, or pass --port
✓ Cloudflare   reachable (64 ms)
✓ Version      newest release
  • ✗ Cloudflare unreachable. Your network blocks the tunnel. Share on the local network with --local, or use a relay you control.
  • ✗ Config. The config file does not parse. Fix it, or delete it to go back to the defaults.
  • ! PATH or Version. See Still showing the old version?

Still stuck? Open a bug report and paste the doctor's output into it.

What visitors see

The link opens a vrok page in the same enamel locker theme as this site — mark, countdown, one Download button. They can view what the browser can show and download the original. A mock of that page is on the homepage. The table below is presentation only. Any type can be shared.

Type Preview
JPG, PNG, WebP, GIF, SVG Image
MP4, WebM, MOV, MKV Video player with seeking
MP3, WAV, FLAC, OGG Audio player
PDF The browser’s PDF viewer
Markdown, JSON, source, logs Rendered or highlighted text
HTML / a folder with index.html The page itself
ZIP, JAR Listing, without extracting
XLSX, DOCX, PPTX, DMG, EXE, APK Download card, correct Content-Type
Anything else Download card

Range requests work end to end, including through a tunnel, so video seeking and resume work. Edits you make on disk show up when the visitor refreshes; vrok does not cache a copy.

Who can open the URL

The HTTP server always binds 127.0.0.1. Reaching it from anywhere else is a tunnel’s job. The banner states the reach so you do not paste a loopback URL into a chat.

Command Who can open it
vrok ./x Anyone with the link (auto tunnel)
vrok ./x --local Your network
vrok ./x --tunnel local This machine only

auto prefers a relay you configured, then an installed client, then fetches cloudflared once into the cache. That download is announced in the terminal. Store a private default with vrok config set tunnel local.

Security and privacy

The full model is in docs/security.md. The short version:

  • Only what you shared is reachable. vrok ./demo.pdf serves that file. The folder it sits in, sibling files, and anything above it are not on the URL. A visitor cannot ask for another path.
  • The visitor never names a file. The URL carries a 128-bit token from crypto/rand. There is no ?path=/Users/you/secret.
  • Path confinement. ../, encoded paths, and symlinks that escape the shared directory all 404 the same way a missing file does. A file replaced by a symlink after you shared it is refused too.
  • Uniform failure. Expired, revoked, exhausted and unknown shares look identical. Probing reveals nothing.
  • Passwords are Argon2id digests in memory, never plaintext on disk. The unlock cookie is HttpOnly.
  • No analytics. Download counts stay in your terminal. Nothing is sent to a vrok server, because there isn’t one on the default path.
  • Default reach is public via Cloudflare’s quick tunnel. The banner says so. Use --local or --tunnel local when it should not leave the building. A public URL still expires.
  • If you share a folder that contains a secret, the secret is shared. vrok confines access to the directory you chose. It does not judge what is in it.

A vulnerability in those guarantees belongs in private reporting, not a public issue.

For teams evaluating vrok

vrok is a program you run, not a service you sign up to. There is no account, no vrok server and no stored copy of anything you share, so there is no vendor holding your data and nothing to certify. What matters is where the bytes travel:

  • Default route. Through a Cloudflare quick tunnel. Cloudflare terminates TLS, so it can see the traffic in transit, and its terms apply.
  • --local keeps a share on your network; --tunnel local keeps it on your machine.
  • Your own relay keeps the whole path on infrastructure you run.

Releases carry SHA-256 checksums, a software bill of materials per archive, and signed build provenance you can check with gh attestation verify. Dependencies are scanned for known vulnerabilities on every change and weekly. The full threat model is in docs/security.md.

Responsible use

You are responsible for what you share and who you send the link to. Anyone with the URL can open it until it expires or you stop it.

  • On the default route your share passes through Cloudflare's free quick tunnels, which are meant for testing and come with Cloudflare's terms. Do not use them to host phishing, malware or anything you have no right to share.
  • For confidential, personal or regulated data, use --local, your own relay, or a password and a short --ttl.

Privacy

The CLI collects nothing. It sends no telemetry and reports no usage. Download counts are kept in the running process and printed in your terminal when the share ends. It connects only to the tunnel or relay you use, to GitHub once to fetch cloudflared if it is missing, and to GitHub and Cloudflare when you run vrok update or vrok doctor. It never checks for updates on its own.

This site sets no cookies. It counts visits with Umami, which keeps no cookies, builds no profile and follows no one to other sites. It records the page, referrer, country and browser, plus a few actions: copying an install command, clicking a download or GitHub link, playing a demo scene, how the star request was answered, whether a guide section helped, and script errors so broken pages get fixed. If your browser sends Do Not Track, nothing is counted.

Fonts are served from the site itself. Your browser asks GitHub's public API for the latest release and the changelog, and remembers in local storage that you dismissed the release banner or the star request, and which sections you already rated.

Share pages set only the cookies they need: one after a correct password, and one that lets a resumed download continue without counting again. Both are signed, HttpOnly session cookies, and stop working the moment the share ends.