1# `nzip`23**Push a directory of HTML from the terminal. Get a four-character URL back.**45_Deno CLI → Cloudflare Worker → R2 + D1. Free-tier-oriented static site hosting with optional6expiration, password protection, owner notifications, and enumeration alerts._78[](https://jsr.io/@nzip/cli)910[github.com/FelineStateMachine/nzip](https://github.com/FelineStateMachine/nzip) ·11[args.io/cat/nzip](https://args.io/cat/nzip)1213```console14$ nzip site push ./demo work:demo --ttl 30d15 bundling ./demo … 3 files, 197 B16 manifest 4a91d75d — 3 new blobs (197 B), 0 deduped17✓ pushed work:demo -> https://12d8.demo.dev/ (expires in 30d, push #1)18```1920## Install2122The CLI ships on [JSR](https://jsr.io/@nzip/cli). With [Deno](https://docs.deno.com/runtime/) on23your `PATH`:2425```sh26deno install -g -A -f -n nzip jsr:@nzip/cli27nzip --version28nzip auth --server https://share.demo.dev29nzip site push ./site work:demo30```3132`nzip auth` prompts for omitted values and saves configuration to `~/.config/nzip/config.json` with33mode 0600. `nzip --version` reports the installed JSR release without requiring authentication; add34`--json` for machine-readable output. Try the CLI without installing it with35`deno run -A jsr:@nzip/cli --help`.3637On another machine, install the CLI and authenticate with the same server and token. Hosted vaults38and sites are already available; only the `nzip site where` breadcrumb registry is machine-local.3940## AI clients and MCP4142This branch adds **`nzip mcp`**, a stateless stdio server for OpenClaw and other MCP clients.43Agents discover vault purposes and enforced policies, choose an appropriate vault, publish HTML,44and update the returned stable ID. No per-session selected site or shell-command parsing is needed.45See [MCP setup, tools, and protocol support](cli/MCP.md).4647Vaults can enforce a maximum TTL and required password protection, and provide an inherited48password for new sites. See [vault purpose and policy](worker/setup.md#vault-purpose-and-policy).49These backend additions require migration `0008`; this branch does not deploy them automatically.5051## How addresses work5253Every share lives at four hex characters. The first digit selects one of 16 registered vaults, and54the remaining three select one of 4,096 randomly allocated sites within that vault.5556```text57https://2a3f.demo.dev/58 |+- site 0xa3f59 +-- vault 0x2 ("work")60```6162Aliases such as `work:demo` are resolved by the authenticated API; public URLs do not expose them.63The control-plane shorthand `https://share.demo.dev/2a3f` permanently redirects to the canonical64site hostname. Each site hostname is a separate browser origin, so its storage, service workers,65and host-only cookies are isolated from other nzip sites. The default passkey relying-party ID is66also the site hostname; see the parent-domain caveat in [SECURITY.md](SECURITY.md).6768| form | example | meaning |69| ----------- | ----------- | -------------------------- |70| address | `2a3f` | direct hexadecimal address |71| vault alias | `work:demo` | alias within a named vault |72| bare alias | `demo` | alias in the temporary default vault |7374## Features7576- **Instant and content-addressed.** Push one canonical manifest and only the blobs the server does77 not already have. Identical files are stored once across every site.78- **Lifecycle-aware by default.** Fresh servers use `personal` (slot `0`, 14 days) for temporary79 shares and `public` (slot `f`, forever) for permanent app origins. Every vault can override its80 own default TTL.81- **First-class lofi apps.** `app init` reserves the final browser origin before the first build;82 `app deploy` validates and publishes a root-scoped lofi PWA without ever recycling that origin.83- **Revertible.** The last ten pushes per site are retained and available through84 `nzip site revert`.85- **Password-protectable.** Password policy is committed atomically with content. Signed, host-only86 per-site cookies are invalidated when the policy changes.87- **Recoverable.** `nzip site cp` reconstructs and verifies the currently hosted bundle.88- **Owner notifications.** Explicitly approved phones receive Web Push notifications with bounded,89 same-origin click targets.90- **Machine-local breadcrumbs.** `nzip site where` locates the source directory recorded during a91 push. An untargeted push from the same logical source updates its unique existing site; a92 directory and its `index.html` count as the same source. Pass `--new` to create another address.93- **Vault guardrails.** Optional `allowVaults` configuration refuses disallowed vaults before94 upload. Raw addresses are mapped to their authenticated vault slot before the same check.95- **Observable abuse controls.** Enumeration is rate-limited and evaluated through bounded,96 privacy-preserving telemetry.9798## Commands99100```text101nzip102├─ --version [--json] show the installed CLI version103├─ auth [--server URL] [--token T] authenticate and save config104├─ status show server and vault status105├─ app106│ ├─ init <alias|vault:alias> reserve a stable app URL107│ └─ deploy build and deploy the configured lofi app108├─ vault109│ ├─ add <name> [--slot N] [--default-ttl 14d|forever|inherit]110│ ├─ update <name> [--default-ttl 14d|forever|inherit]111│ ├─ ls list vaults112│ └─ default <temporary|permanent> <name>113├─ site114│ ├─ push <dir|file> [target] [--new] [--ttl …] [--password PW | --no-password]115│ ├─ cp <target> [dir] [--overwrite] copy a hosted bundle116│ ├─ show <target> show site details117│ ├─ policy <target> [--ttl …] [--password PW | --no-password]118│ ├─ ls [vault] list sites119│ ├─ where <target> print this machine's source directory120│ ├─ rm <target> [--yes] delete a site121│ └─ revert <target> [--to N] [--list] inspect or restore push history122└─ notify123 ├─ send <body> [--title TEXT] [--open TARGET] [--tag TEXT]124 ├─ test send a diagnostic notification125 ├─ pair allow device pairing for 10 minutes126 ├─ approve <code> --name NAME [--yes]127 ├─ devices [--all] list notification devices128 └─ revoke <device-id> [--yes] revoke a notification device129```130131Password and TTL values are committed with content. New-site TTL resolves in this order: explicit132`--ttl`, existing-site expiry, vault default, then the global 14-day fallback. Changing a vault133default never rewrites existing sites. `public` names a durable namespace, not an access policy;134sites there may still be password-protected. `status --json` and `vault ls --json` expose135`defaultVaults`, `globalDefaultTtl`, and each vault's effective default for agent planning. Push JSON136reports `ttl` and `ttlSource`. Directory pushes skip dotfiles and `node_modules` and honor a137`.nzipignore` file containing one glob per line.138139Content revisions use `site push`; access and expiry changes use `site policy`. With no explicit140target, `site push` reuses a unique unexpired local breadcrumb for the same logical source. It stops141when multiple sites match instead of guessing. Pass the intended target to resolve the ambiguity,142which rebinds later untargeted pushes to that site, or pass `--new` only when a separate URL is143desired.144145## Host a lofi app146147Reserve the production origin before configuring credentials or building:148149```sh150cd field-notes151nzip app init field-notes152# add the printed origin to src/app.ts credentialOrigins153nzip app deploy154```155156`app init` defaults to the permanent vault, is idempotent, and writes a token-free157`nzip.app.json` that is safe to commit. Before the first deploy, the reserved URL serves a small158not-deployed page. `app deploy` runs the configured Deno build task with `LOFI_BASE_PATH=/`, verifies159the lofi build identity, PWA files, stable credential origin, and optional passkey RP ID, then160publishes `dist/`. It also mirrors lofi's generated CSP as a response header. Deleting or expiring161app content does not free its address: the origin reservation remains a permanent tombstone because162service workers, OPFS, IndexedDB, and passkeys bind application identity to that origin.163164See [`cli/README.md`](cli/README.md) for detailed CLI behavior and examples.165166## Owner notifications167168Pairing is closed by default. Open a 10-minute window, then tap the temporary `pair` action on the169deployment root and approve the displayed code:170171```sh172nzip notify pair173nzip notify approve ABCD-1234 --name "Personal phone"174```175176Wait for `paired` before installing the PWA. In the installed app, tap `notifications off` to177request permission and attach the subscription. Send a notification with:178179```sh180nzip notify send "Build finished" --open work:report181```182183Notification content may be visible on a lock screen. Never include passwords, tokens, private URLs,184or sensitive personal information. See [SECURITY.md](SECURITY.md) for the enrollment and delivery185trust boundaries.186187## Self-hosting188189nzip is self-hosted; there is no bundled public service. The Worker uses an exact control hostname,190a wildcard site route, R2, D1, rate-limit bindings, and optional Email Routing and Web Push191configuration.192193Follow [`worker/setup.md`](worker/setup.md) for provisioning, migrations, secrets, notification194setup, deployment, and operational checks. The system is designed for small personal deployments on195Cloudflare's included usage; see the196[architecture budget notes](ARCHITECTURE.md#free-tier-design-target) before exposing a busy or197multi-tenant instance.198199## Project documentation200201- [ARCHITECTURE.md](ARCHITECTURE.md) — components, push protocol, caching, storage, background work,202 observability, and resource budgets.203- [SECURITY.md](SECURITY.md) — trust boundaries, pairing security, enumeration controls, reporting,204 and monitoring.205- [CONTRIBUTING.md](CONTRIBUTING.md) — development setup, checks, local Worker workflow,206 documentation conventions, and releases.207- [`worker/setup.md`](worker/setup.md) — production Cloudflare deployment and upgrades.208- [`cli/README.md`](cli/README.md) — CLI installation and command reference.209- [`shared/README.md`](shared/README.md) — shared package contract and usage.210211MIT licensed.