---
name: quick
description: >-
  Publishes and manages static sites on the internal "quick" hosting with the `quick` CLI.
  Use it when the user wants to put a folder of HTML/assets online and get
  a <name>.<domain> URL, republish a site, change its visibility (public,
  access code, company SSO, only some people), share it with chosen
  colleagues, lock or delete it, check its status,
  create deploy tokens for CI/agent environments, attach a custom domain, or understand why a deploy
  excludes certain files. Covers mirror deploys, .quickignore, the 404.html /
  200.html / clean URL / _redirects conventions (redirects, SPA rewrites and
  same-origin API proxying), per-path response headers with _headers, the
  noindex on quick addresses, login, multiple servers, deploy tokens and status.
---

# quick CLI

`quick` is the CLI for an internal static hosting: you publish a folder of
HTML/assets and get `https://<name>.<domain>`. By default a site is visible
only to accounts in the company domain (company SSO); you can open it to the public,
protect it with a code, limit it to yourself or to chosen colleagues, or lock it
against overwrites.

Access codes have at least 8 characters. Setting one prints the code and a
`link:` (`https://<name>.<domain>/?quick_code=<code>`) that opens the site without
typing it: share the link, or the address plus the code. The link carries the code, so
treat it like the code. Changing the code signs out everyone who entered the old one.
`quick access <site>` shows the current code and link again to whoever can change the
site (codes set before this was possible show as `not visible`: set a new one).

Colleagues signed in with the company SSO open a code site without the code (the code
page offers them a sign-in link): the code is for people outside the company. To ask
colleagues for it too, add `--code-only` (`quick access <site> code --code-only`).
It needs a site colleagues can't manage, otherwise any of them could read or change
the code anyway: on a server in free ownership mode (the default) a locked site
(`quick lock` first), and unlocking it turns the option off; in shared or owned
mode, any site with a recorded owner. Administrators always get in. Without the flag
the server's default applies, also when the code is set again (administrators can
make it ask colleagues by default, and lock new sites by default);
`--code-only=false` lets colleagues in whatever the default. `quick private` and
`deploy --private` print which of the two applies.

A site exists from its first deploy, which records its owner: visibility (`quick access`,
`publish`, `private`), lock and deploy tokens are refused before it
(`publish it first with quick deploy`). To publish a site already protected, use
`quick deploy <site> --private` or `--public`, which set it right after the first deploy.

The server configures itself: the only required input is the URL, via `--server`
or the `QUICK_SERVER` variable. After the first deploy, the folder remembers site
and server in a `.quick` file, so later commands don't need
arguments.

## First use

```bash
export QUICK_SERVER=https://quick.example.com   # once (or use --server)
quick login                                     # opens the browser to log in
quick deploy my-site ./build                    # -> https://my-site.quick.example.com
```

## Commands

| Command | What it does |
|---|---|
| `quick` | Overview (server, login, linked site) + command list |
| `quick status` | Site status: real visibility, lock, and what would be deployed |
| `quick login` | Log in (once per server; the login is remembered and that server becomes the default) |
| `quick servers [use <server>]` | Known servers with login status; `use` changes the default |
| `quick deploy [<site>] [folder]` | Publish a folder (default: the current one) |
| `quick ignore [folder]` | Create an editable `.quickignore` with the defaults already inside |
| `quick access [<site>]` | Who can see the site: level, owner, and the people list (if you are on it) |
| `quick access [<site>] team \| public \| code [--code X] [--code-only[=false]] \| only-me` | Set who can see it: everyone at the company (default), public, by code (min 8 characters, prints a ready link; colleagues signed in with company SSO get in without it unless `--code-only`, locked sites only), or only you |
| `quick access [<site>] add <email>...` / `remove <email>...` | Share with (or stop sharing with) chosen people (company accounts; outside addresses too if the server sends email); `add` limits the site to the owner + the list |
| `quick publish [<site>]` | Open to the public (no SSO) |
| `quick unpublish [<site>]` | Back behind company SSO (default) |
| `quick private [<site>] [--code X]` | Access by code (generated if absent; min 8 characters; prints a ready link) |
| `quick lock [<site>]` / `quick unlock [<site>]` | Lock/unlock overwrites (owner only) |
| `quick token create [<site>] --name github-actions [--expires 90d]` | Create a site-scoped deploy token |
| `quick token list [<site>]` | List deploy tokens for a site |
| `quick token revoke [<site>] <token-id>` | Revoke a deploy token |
| `quick domains [<site>]` | List custom domains and the DNS records still missing |
| `quick domains add [<site>] <domain>` | Attach a custom domain (e.g. www.example.com) |
| `quick domains rm [<site>] <domain>` | Detach a custom domain |
| `quick limit [<site>] [<size>\|default]` | Show the site's size limit (100 MB by default); administrators change it, e.g. `1.5GB` |
| `quick rollback [<site>]` | Restore the previous version (with confirmation; `--yes` skips it; `--takeover` even during someone's deploy) |
| `quick delete [<site>]` | Delete the site (irreversible, with confirmation) |
| `quick upgrade [--check]` | Update the CLI to the latest release (`--check` only checks) |

`<site>` is optional if the folder has a `.quick` file: in that case the name
and server come from there. Without `.quick` and without a name, `quick deploy` uses the name
of the current folder; the other commands stop and ask for the name.
With a single argument, `quick domains add|rm` and `quick token revoke` read it as
the domain or token ID and take the site from `.quick`.

## Multiple servers

The CLI can work with several quick servers (e.g. one per company). Each server
keeps its own login. The server is chosen in this order: `--server`, then
`QUICK_SERVER`, then the folder's `.quick`, then the default server.
`quick login --server X` makes X the default; `quick servers` shows which one it is.
If the folder is linked to the same site on another server, the CLI asks before acting.

A server you have already used can be named by a word of its address:
`--server acme` means `quick.acme.com`, `quick servers use acme` makes it the default. A new server needs its full address the first time. The first deploy
from a folder writes the server into `.quick`, so later commands there need no flag:

```bash
quick deploy my-site --server acme    # once; then just: quick deploy
```

## Deploy: it's a mirror

**The deploy replaces the entire content of the site**, it does not add: files not
present in the package are removed from the site. Consequences:

- To update a single file you still republish the whole folder, but the upload
  is incremental: only the parts of files the server doesn't already have travel
  (content-defined chunks, ~64 KiB). Republishing a big build where part of a
  file changed, or with renamed files, sends little; the CLI reports it as
  "uploaded X of Y". Against an older or busy server it uploads the whole folder.
  Builds compressed as a whole (e.g. Unity Gzip/Brotli compression) change
  everywhere on each build, so they get little benefit.
- A deploy from an empty folder would wipe the site: the CLI **blocks** it
  (use `--force` to empty it on purpose).
- Before publishing, the CLI shows a summary (file count, size) and
  asks for confirmation. Skip the prompt with `--yes`; in non-interactive contexts
  without `--yes` the deploy is refused, for safety.
- One deploy at a time per site. While someone is publishing it, another deploy
  of the same site (yours from another terminal too) or a rollback is refused
  with `<who> is publishing this site (since 14:02); retry in a moment or use --takeover`.
  The site frees up as soon as that deploy ends; a deploy interrupted with
  Ctrl-C or that fails gives it back at once (so you can rerun right away), one
  whose CLI was killed or lost the network after 2 minutes without activity.
  A deploy interrupted before publishing publishes nothing; once publishing
  has started it may still complete (the CLI then says the publish "may have completed": check
  the site before rerunning). Wait and retry; use `--takeover` (on `deploy` or `rollback`) only when
  you know the other deploy is stale or must be overridden, never by reflex:
  the other deploy then stops with `this deploy lost the site ... nothing was published, run it again`
  (unless it was already publishing: then it completes and yours publishes right after).
  `--force` never takes over. Deleting the site always works and ends a deploy in
  progress (if a publish has already started, the delete waits for it).

Useful flags: `--dry-run` (show what would be deployed without publishing),
`--yes`, `--force` (empty the site on purpose), `--takeover` (take over from a
deploy in progress), `--public` / `--private[=code]` (visibility right after the deploy).

## CI and web agents

For non-interactive environments such as GitHub Actions, Claude Code web or
other hosted agents, create a site-scoped deploy token:

```bash
quick token create my-site --name github-actions --expires 90d
# Save the printed value as a secret named QUICK_API_TOKEN.
QUICK_API_TOKEN=qk_... quick deploy my-site ./build --yes
```

Deploy tokens are intentionally narrow: one token is valid for one site and only
for deploys. It cannot delete, rollback, change visibility/lock, or create other
tokens. The cleartext token is shown once; later use `quick token list` to see token
IDs and `quick token revoke` to remove them. Expiry choices are `30d`, `90d`,
`180d`, `365d`, or `never`.

## Custom domains

`quick domains add my-site www.example.com` attaches a domain and prints the DNS
record to create at the domain's DNS provider (any provider; DNS only, no CDN
proxy): `www.example.com CNAME my-site.<domain>`. An apex like `example.com`
can't CNAME: use ALIAS/CNAME flattening or the same A record, plus a TXT record
`_quick.example.com` with the site name (`my-site`), which tells quick which
site the domain is for. `quick domains` prints both.

The domain stays "waiting for DNS" until the record resolves, then becomes
active on its own (`quick domains my-site` shows the state); the HTTPS certificate
is issued on the first visit. A domain belongs to one site; it moves to another
site only when its DNS points there (for an apex: when the TXT names it). Public and access-code sites are served on
the custom domain; company-SSO sites redirect visitors to their regular
`<site>.<domain>` address, since sign-in doesn't carry across domains.

## What is NOT published

Exclusions are decided in three tiers:

1. **Security (always, not overridable):** hidden files (`.git`, `.env`, `.quick`…,
   except `.well-known`) and secrets (`*.pem`, `*.key`, `id_rsa`, keystores).
2. **Convenience (default, overridable):** `node_modules/`, `vendor/`, `*.log`, temporary files.
3. **`.quickignore` (if present):** becomes the source of truth for the convenience
   exclusions (gitignore syntax, with `!` to re-include). Create it with `quick ignore`.

Use `quick status` or `quick deploy ... --dry-run` to see included/excluded files.

## Served site conventions

- `index.html` is a folder's index. `/about` serves `about.html`;
  `about/index.html` is served at `/about/`.
- HTML pages have canonical URLs: `/about.html` -> `/about`,
  `/about/index.html` -> `/about`, and if that is a folder,
  `/about` -> `/about/`.
- `404.html` at the root or in the nearest folder: page shown (with status 404)
  for nonexistent paths.
- `200.html` at the root: app shell for SPAs; served (status 200) for
  any route that doesn't match a file. Without it, missing paths
  give a real 404 (no silent fallback to the home page).
- `_redirects` at the root (Netlify-style): per-path rules, applied only
  when no file matches (files always win) and before `200.html`:

  ```
  /old    /new                            301   # redirect (302/307/308 too; 301 if omitted)
  /*      /index.html                     200   # local rewrite: SPA catch-all
  /api/*  https://api.example.com/:splat  200   # proxy: call a non-CORS API same-origin
  ```

  Only a trailing `/*` wildcard, referenced as `:splat`. Proxying is a
  pass-through to public https hosts only (no private IPs, no quick subdomains,
  no injected credentials) and follows the site's visibility. The
  `_redirects` file itself is never served.
- `_headers` at the root (Netlify-style): response headers per path, a path
  line then indented `Name: value` lines:

  ```
  /*
    X-Frame-Options: DENY
  /assets/*
    Cache-Control: public, max-age=31536000, immutable
  ```

  Paths as in the address bar (`/about`, not `/about.html`), only a trailing
  `/*` wildcard. Every matching rule applies; a header from several rules is
  joined with `, `. Applies to the site's files (also `200.html`/`404.html`),
  not to redirects or proxied responses. Headers quick owns are ignored:
  `Set-Cookie`, auth (`WWW-Authenticate`, `Authorization`, `Proxy-*`),
  `Content-Type`/`Content-Encoding`/`Content-Length`/`ETag`/`Last-Modified`
  and other transport headers, `Location`, `X-Content-Type-Options`,
  `Strict-Transport-Security`, `Access-Control-Allow-Credentials`, `X-Quick-*`,
  `X-Forwarded-*`. The `_headers` file itself is never served.
- Every response on a `<site>.<domain>` address has
  `X-Robots-Tag: noindex, nofollow` (assets too); it can't be turned off, and an
  `X-Robots-Tag` in `_headers` only adds directives. For a site that must be
  indexed, attach a custom domain (`quick domains add`): quick adds no noindex
  there. Don't add `Disallow: /` to `robots.txt`: crawlers then can't see the
  noindex. Pages don't need their own robots meta tag on quick addresses.
- Precompressed assets work as-is: `x.js.br`, `x.wasm.gz`, `x.data.br`,
  `x.json.gz` are served with the right type and Content-Encoding. Unity WebGL
  builds deploy unchanged with any compression setting (Brotli, Gzip, or
  decompression fallback `.unityweb`, served with Content-Encoding too
  so the browser decompresses it); no server config needed.
- Every site can import the browser library `/__quick/quick.js` (ESM, types at
  `/__quick/quick.d.ts`). `await quick.id.me()` returns the signed-in user
  (`{email}`) on SSO and chosen-people sites and for colleagues on access-code sites, `null` on public sites and for whoever entered a code. The
  `/__quick/` path prefix is reserved for quick: a deploy containing a `__quick/` folder at the root is refused.

## Notes

- Every site has a size limit per version (100 MB unless the server says
  otherwise; `quick limit` shows it). A deploy over it fails with 413 and nothing
  changes: tell the user an administrator has to raise the site's limit (the
  message names them), don't try
  to split the site or work around it. A 507 means the server is short on disk
  space: also a matter for an administrator.
- New subdomains are immediate; visibility changes are instant.
- A **locked** site can be overwritten or deleted only by its owner.
- **Administrators** (set on the server) see and manage every site, chosen-people and
  only-me ones included, and can do whatever the owner can. "Only me" therefore means
  "me and the administrators": say so if the user relies on it for privacy.
- **Chosen people** (`quick access <site> add|remove|only-me`): visitors sign in with SSO and must be the
  owner (the site's creator) or on its list; anyone else gets a "restricted" page that names the owner,
  never the list. Emails must be company accounts (max 100 per site), unless the server can
  send email (QUICK_SMTP_URL): then outside addresses are accepted too, and those people
  ask for a sign-in link on the site's sign-in page (works once, 15 minutes; keeps them
  in on that browser for 7 days; removing them locks them out at once). Without email,
  `add` of an outside address is refused with a message saying so. Only the owner can limit a site
  to chosen people, change who sees such a site (in any ownership mode), or delete it;
  other users get `only the owner (x) can change who can see this site`. Deploys are unchanged:
  whoever may deploy the site still can, even without seeing it (then, and with deploy
  tokens, the whole folder is uploaded every time: no delta). Such sites (dashboard,
  `/api/sites`), their deploy tokens and custom domains are shown only to their people.
  `publish`/`unpublish`/`private` still work as shortcuts
  (`unpublish` = `access <site> team`).
