nzip is a personal, single-owner service. Its short public URLs are convenient addresses, not secrets. Security comes from explicit authentication, password protection where needed, bounded public surfaces, owner-approved device enrollment, and observable abuse controls.
Sites execute at https://<address>.<site-domain>/, giving every address a
distinct browser origin. Origin-scoped localStorage, IndexedDB, OPFS,
CacheStorage, service workers, and passkey defaults are isolated between sites.
nzip's unlock cookies are host-only. The control-plane shorthand
https://<control-origin>/<address> is redirect-only and never serves artifact
bytes.
Subdomain isolation is a browser security boundary, not a content-safety review.
A hosted application still has its own origin's ambient authority, can make
cross-origin network requests where a target permits them, and can deceive its
visitors. Artifact responses disable document.domain relaxation so sibling
site hostnames cannot opt back into a shared origin.
Cookies and WebAuthn have additional domain rules. Until n.zip is accepted
into the Public Suffix List, an arbitrary site can
set a Domain=n.zip cookie, and it can request the parent RP ID n.zip. It
cannot request a sibling RP ID such as ffff.n.zip. Applications should use
__Host- or otherwise host-only cookies and explicitly set
rp.id = location.hostname; on 2a3f.n.zip, the RP ID is 2a3f.n.zip. Do not
use passkeys or application-defined cookies on the control origin, or claim
hostile-tenant credential isolation, until that Public Suffix List boundary
ships in target browsers.
Do not disclose a vulnerability in a public issue. Use the repository's private security advisory channel when available, or contact the repository owner privately. Include affected versions, reproduction steps, impact, and any suggested mitigation.
- Every
/api/*management route requires the deployment-wide bearer token. - The bearer token stays in Worker secrets and the CLI's mode-0600 configuration file; browsers and installed notification apps never receive it.
- Public site addresses occupy only 16 bits and should be treated as enumerable.
- The wildcard route accepts only exact four-character lowercase hexadecimal site hostnames. Unknown wildcard hosts cannot dispatch management or notification APIs.
- Artifact hostnames are mutually isolated browser origins. The exact control
origin alone owns
/api/*, notification enrollment, and the root notification PWA. - Password-protected sites use PBKDF2 hashes and signed, versioned cookies.
Changing password policy invalidates existing cookies immediately. Unlock
cookies use the
__Host-prefix and are host-only, so one site cannot plant or receive another site's unlock state. - Notification click targets are same-origin paths pinned to an existing site manifest. Arbitrary external URLs are rejected.
- Web Push subscription endpoints must match configured exact HTTPS provider origins. Wildcards, credentials, IP literals, alternate ports, and the deployment's own origin are rejected.
The deployment runbook includes an optional Free-plan WAF custom rule that mirrors the hostname shape at the edge. Worker validation remains the source of truth, so correctness does not depend on the WAF rule or paid regular-expression support.
The Worker disables Cloudflare shared-edge caching on every response with
Cloudflare-CDN-Cache-Control: no-store. This is a required origin-isolation
control: the exact custom domain and wildcard Worker route can otherwise reuse
one cached public response for the same path on another hostname. Public sites
still advertise short browser caching and support ETag revalidation.
Pairing is closed by default. An authenticated owner opens a fixed 10-minute
window with nzip notify pair. The public root reveals the pairing action only
during that window, and the Worker enforces the same deadline when creating an
enrollment.
Pairing codes are short-lived, normalized, and stored only as keyed hashes. A
code identifies a pending device but cannot approve itself; approval still
requires the owner bearer token. Device claims use HttpOnly, Secure,
SameSite=Lax, host-only cookies and transition through pending, approved, and
active states before notifications can be delivered.
The root pairing surface cannot be framed. Pairing-state reads are same-origin and rate-limited, and pending pages do not expose a second enrollment action.
The address space is intentionally small, so nzip observes and limits enumeration rather than claiming addresses are unguessable.
flowchart TB
REQ(["request"]) --> SURFACE{"request surface"}
SURFACE -- "/api/*" --> TOKEN["bearer check"]
SURFACE -- "GET /xxxx" --> ENUM["enum limiter"]
SURFACE -- "POST unlock" --> UNLOCK["password limiter"]
TOKEN --> API["owner API"]
ENUM --> SERVE["site lookup"]
UNLOCK --> SERVE
SERVE --> GATE{"password protected?"}
GATE -- "yes" --> COOKIE["PBKDF2 + cookie"]
GATE -- "no" --> PUBLIC["public response"]
ENUM -. "HMAC identity" .-> PROBES[("D1 probe windows")]
PROBES --> EVAL["incident evaluator"]
EVAL --> OUTBOX[("email outbox")]
OUTBOX --> EMAIL["verified email"]
EMAIL --> INBOX(["operator inbox"])
Raw client IPs are neither logged nor stored. Scanner identities are HMAC-derived. Probe rows are deduplicated, capped per scanner and Cloudflare location, and pruned after seven days.
The evaluator opens or escalates incidents on these signals:
| severity | signal |
|---|---|
| warning | one scanner tries 20 distinct addresses in five minutes |
| warning | 128 addresses from 10 scanners with at least 90% misses |
| confirmed | an enumeration request reaches the rate limiter |
| confirmed | a live hit follows sequence evidence or occurs during an open incident |
Duplicate alert email is suppressed unless severity increases, volume doubles, a live site is hit for the first time, or a new vault is targeted after 30 minutes. Active incidents summarize at most hourly and resolve after three quiet windows. Alert payloads are committed before delivery and retried with stable notification identifiers.
- Filter Workers Logs on
event = "security.request"for sampled request context. - Watch D1 row writes for the enumeration-telemetry budget.
- Keep the Email Routing destination verified and test it through the
authenticated
/api/security/test-alertendpoint. - Treat notification titles and bodies as lock-screen-visible. Never include credentials, private URLs, or sensitive personal information.
Deployment configuration and security-alert setup are documented in
worker/setup.md.