cat / nz
clone

push a directory, get a url: sh + one Cloudflare Worker + KV

main
nz / README.md
README.md 3.3K source

nz

Push a directory of HTML, get a url back.

$ nz push ./report --password meow
expires 2026-11-17T19:40:02.000Z, password protected
https://k3x9q.example.com/

Minimalism

nz is a few files you can read in one sitting:

file what
worker.js Cloudflare Worker, plain JS, no dependencies, no build
nz POSIX sh CLI: tar + curl, nothing to install
nz.ps1 the same CLI for PowerShell on Windows
test.sh end-to-end test against wrangler dev

A site is one KV entry: the value is a tar of the directory, and the metadata is { exp, pw }. KV expiration deletes it when the TTL runs out.

Install

wrangler kv namespace create SITES   # paste the id into wrangler.jsonc
# set DOMAIN + routes in wrangler.jsonc, add a proxied DNS record: * AAAA 100::
wrangler secret put TOKEN
wrangler deploy

ln -s "$PWD/nz" ~/bin/nz
mkdir -p ~/.config/nz && printf 'NZ_SERVER=https://example.com\nNZ_TOKEN=...\n' > ~/.config/nz/config

On Windows, put nz.ps1 somewhere on your PATH and write the same config. It needs Windows 10 or later, which ships tar.exe and curl.exe:

Unblock-File .\nz.ps1   # if it came from a download
New-Item -ItemType Directory -Force ~\.config\nz | Out-Null
"NZ_SERVER=https://example.com`nNZ_TOKEN=..." | Set-Content -Encoding ascii ~\.config\nz\config
nz.ps1 push .\site --ttl 7d --password meow

CLI

nz push <dir|file> [--ttl 14d|never] [--password PW] [--new]
nz update <id> [dir|file] [--ttl 14d|never] [--password PW | --no-password]
  • push updates the site last pushed from the same path (~/.config/nz/sites, one id<TAB>path per line), or makes a new one if there is none, it expired, or --new.
  • update targets an id. Without a path it only changes ttl/password.
  • nz.ps1 takes the same commands, and also accepts PowerShell-style -ttl, -password, -new.
  • A single file is published as index.html. Dotfiles and node_modules are skipped.
  • TTL: 30m, 12h, 14d, 2w, never. Default DEFAULT_TTL (14d). Omitted on update = kept.
  • The url goes to stdout and details to stderr, so open "$(nz push .)" works.

API

The CLI is a thin wrapper around this. Everything lives on the apex host:

tar --format=ustar -cf - -C site . |
  curl -H "authorization: Bearer $TOKEN" -H 'nz-ttl: 7d' -H 'nz-password: meow' \
       --data-binary @- https://example.com/                # POST: new site -> 201
curl -X PUT ... --data-binary @site.tar https://example.com/k3x9q   # replace content
curl -X PUT -H "authorization: ..." -H 'nz-password;' https://example.com/k3x9q  # policy only

The response is the url on line 1 and a details line on line 2. nz-password; (empty) removes the password. Omitted headers keep their current value on PUT.

Sites live at https://<id>.DOMAIN/, so each one is its own browser origin. Password-protected sites show a form. Unlocking sets a host-only __Host-nz cookie that lasts 7 days and is signed over the password verifier, so changing the password logs everyone out. Unlock attempts are rate-limited to 5 per minute per ip and site.

License

0BSD