Wisp is in pre-release, self-hosted since GitHub suspended us. Install it, or see the changelog.

Wisp

CLI Reference

wisp help <command> or wisp <command> --help shows one command's rows. App commands run from the app's folder.

Commands

CommandWhat it does
wisp new [name]create an app (options below)
wisp dev [--port|-p <n>]run the app, rebuild and reload on save (port 3000). With --port 0, the system picks a free port
wisp dev --httpsserve over TLS with a self-signed certificate made once in .wisp/dev-cert.pem and .wisp/dev-key.pem (renewed when you delete them, when the address in HOST is new, or when it expires). Browsers ask once to trust it. For secure-context APIs (camera, clipboard, service workers) on a LAN address with HOST=0.0.0.0. Not with --target
wisp dev with [package.metadata.wisp.dev.proxy]forward paths to other http:// or https:// servers, and with [package.metadata.wisp.dev] run start companion commands (Config)
wisp dev --target node|bun|denothe dev loop through that host's glue, built into .wisp/emulate/<host> (refuses vercel, netlify, lambda, native, cloudflare, pages)
wisp preview [--port|-p <n>] [--fast]build the release binary and run it as deployed (port 4173). It checks the port and waits for the app on HOST. --port 0 is an error
wisp preview --static|--spa [--out dist] [-p <n>]export the site and serve it like a static host (no server code, POST is 405). --spa falls back to index.html
wisp buildone release binary with CSS and static files inside
wisp build --sourcemapthe same, with source maps for browser code
wisp build --fastquicker ThinLTO build for CI and iteration.
wisp clean [--cache]cargo clean of target/. --cache also deletes the shared build cache (WISP_BUILD_CACHE), only a folder a build marked
wisp build --analyzeeach route's JS, CSS and wasm bytes, raw and gzipped. Builds nothing
wisp build --static [--out dist]the pages as plain files for a static host. Removes files of an earlier export it no longer writes. Warns of dead links and #ids (seo-link, seo-anchor)
wisp build --static --allow-dead-linksa link to a missing page or to an #id that the linked page lacks only warns (also without --static, for the PRERENDER crawl)
wisp build --no-crawlthe prerender and --static follow no link: only the routes and entries() say which pages are rendered, and no link is checked
wisp build --spa [--out dist]the same, and index.html draws the SSR = false pages
wisp build --target|-t <host> [--edge] [--out|-o <dir>]a folder for cloudflare, pages, deno, vercel, netlify, node, bun or lambda. --edge uses edge functions on vercel or netlify
wisp client [-o|--out client.ts]a typed TypeScript client of the +server.rs endpoints
wisp openapi [-o|--out openapi.json]print or write the OpenAPI 3.1 document
wisp openapi --checkfail if the committed file is stale (CI)
wisp check [--types] [--rust] [--target <host> [--edge]] [--explain-imports]check routes and templates without compiling, and warn of SEO gaps (seo-title, seo-description, seo-canonical, seo-h1, seo-link: see /docs/design-pages). --types runs tsc (without TypeScript it prints Type checking was SKIPPED, not passed; set WISP_TSC). --rust runs cargo check. --target <host> warns of what that host cannot run. --explain-imports lists names used with no use line. Syntax errors in scripts show file:line:col
wisp test [--browser] [args]cargo test with its args. --browser adds browser tests
wisp fmt [paths]format .wisp files
wisp fmt --checkname unformatted files and fail
wisp fmt --stdin [path]format stdin to stdout, for editors
wisp package [args]in a component library: check it, then cargo package
wisp route add <path> [kind]write a route: page (default), form, layout, server, rest (or api) or crud. The file is written as wisp fmt leaves it. A folder that the route scanner skips is refused with its reason: a name that ends in .bak, .tmp or .orig, a name that starts with a dot (.well-known is a route and is written), and a Windows device name such as con. A path that matches the same URLs as an existing route (/[[opt]] beside /) is refused and nothing is written. So is a rest route whose path has an id parameter (/x/[id]): a rest route serves its own /[id]
wisp route add <path> crudlist, new and edit forms for a table, with its #[model] and Table::saved() in src/db.rs (added unless the file declares a type of that name: a comment or string with the name does not count). If its routes match the URLs of another route, or a file write fails, it writes nothing and deletes what it made. A model that would take the name of a type the file uses (/tables is Table, /strings is String) gets Row added: TableRow
wisp route listeach route's methods, URL and file
wisp component add <Name>a blank src/components/Name.wisp
wisp component listthe components with file and props
wisp deploy init <host> [--force]a GitHub Actions workflow that deploys on push. It runs wrangler, vercel and netlify-cli at exact versions. Each wisp release sets them. Actions use tags: pin each uses: to a commit SHA for a stricter build
wisp deploy init fly|render|railway|cloud-run [--force]that host's config and a Dockerfile, with a health check on /_wisp/health, a kill timeout past the drain, the host's client IP header where it is safe, and saved tables on one instance
wisp deploy init docker [--onion] [--force]a Dockerfile and .dockerignore. --onion adds Tor files for a Tor onion service
wisp deploy init tor [--force]write deploy/tor/torrc (lines for /etc/tor/torrc: HiddenServiceDir /var/lib/tor/<app>/, HiddenServicePort 80 127.0.0.1:3000) and deploy/tor/<app>.env (HOST=127.0.0.1, WISP_ONION=on) for tor and wisp service install, with no container (Tor onion service)
wisp npm add <pkg>[@version]add an npm package to package.json, no Node needed. wisp build pins each downloaded file by SHA-256 in npm.lock
wisp npm remove <pkg>remove one
wisp npm listthe app's npm packages and versions
wisp recipe add <name> [--force]apply the recipe add/<name>/recipe
wisp recipe listthe recipes in add/
wisp ui add <name…> [--force]copy components (button, dialog, tabs…) into src/components
wisp ui listthe components wisp ui add has
wisp service install|uninstall|start|stop|statusrun the release binary as a systemd unit, launchd daemon or Windows scheduled task. Options: --name, --user or --root (install needs one), --port, --dry-run. install on an installed service upgrades it: the new build runs after it. On Windows the task command must be 261 characters or fewer. status exits 1 with not installed when there is no service (Linux: not running for a stopped unit)
wisp lspthe language server, over stdio. A bad header or Content-Length gets a JSON-RPC error (-32700). The server then reads on from the next Content-Length
CLI downloadscurl stops at 512 MiB for a file and 64 MiB for a body in memory. It checks the size against Content-Length: a response with no length is not capped
wisp update-docsupdate AGENTS.md to this Wisp
wisp upgrade [--check | --undo | --force-undo]install the newest release and, in an app, bump its Wisp dependencies to it (see Updating)
wisp changelog [version | --since <version>]print what changed from the installed version to the newest, one version, or everything after one (see Updating; the same text is /changelog)
wisp mcpdocs, routes, components and checks for AI agents (MCP, stdio)
wisp --helpthe commands by group (-h). wisp help <command> [verb] lists one command's rows
wisp --versionthe version (-V)

Options for Wisp New

OptionWhat it does
-t, --template demo|minimal|apia demo app, one empty page, or a JSON API (also --api)
--tailwind, --no-tailwindadd Tailwind CSS, or not
--ts, --no-tsTypeScript only (script = "ts"). On by default. In wisp dev a violation is a warning
--strict, --no-strictwarnings are build errors (strict = true). In wisp dev they are warnings, so a pasted docs example runs. On by default
--typecheck, --no-typecheckwisp build and wisp check run tsc (typecheck = true). Off by default
--git, --no-gitcreate a git repository, or not
--install, --no-installcompile dependencies now, or on the first wisp dev
-y, --yesuse the default for each option not given
--liba component library, not an app. It asks nothing

An app also gets a rust-toolchain.toml that pins the Rust version of the Docker image (wisp deploy init docker). Local builds and the image then use one compiler. See Reproducible builds. An option given both ways (--ts --no-ts) is an error. The folder name is the package name, so it takes ASCII letters and digits only: über is refused.

Updating

wisp upgrade installs the newest release (cargo install --git https://wisp.ar0.eu/ --tag <tag> wisp-web --force). The git tag of the release is the trust anchor: cargo gets the source at that tag and builds it. In an app folder it also sets tag = "<tag>" on the Wisp git dependencies in Cargo.toml, runs cargo update for wisp-web-rt, wisp-web-build, wisp-web-macros and wisp-web-shared, then runs wisp check and wisp update-docs with the new CLI. It reports each step.

The app upgrade is all or nothing. Before it changes a file, wisp upgrade copies Cargo.toml, Cargo.lock and AGENTS.md to target/wisp-upgrade. First it checks that the installed CLI shows the new version, and stops with no change when it does not. When a step fails, it puts the copies back and says so. When wisp check fails, fix the check errors, then run wisp upgrade again. After a good upgrade, wisp upgrade --undo puts back the files of the last upgrade and deletes the copies. It refuses when Cargo.toml changed after the upgrade, as the undo would lose that change: wisp upgrade --force-undo undoes all the same. The copies are under target/, so cargo clean deletes them. The CLI stays at the new release: to get the old one back, run the install command with the old --tag.

wisp upgrade --check only reports. It exits 1 when a newer release is out, so CI can use it. WISP_RELEASES_URL sets the release list URL, an https URL (default: the Forgejo API at code.wispweb.dev). The answer must have the Forgejo or GitHub releases JSON shape. wisp new pins a new app to the tag of the CLI that made it, so an update is always a change you make.

wisp dev, wisp build and wisp new look up the newest release at most once a day (the Forgejo API of the repository, prereleases included; the answer is cached in the user cache folder as wisp/latest-release). They print one line on stderr when the CLI, or the wisp-web-rt version in the app's Cargo.lock (a git dependency; a path dependency on a Wisp checkout is never flagged), is older than that release:

! wisp 1.0.0-pre.2 is out of date: 1.0.0-pre.3 is out. Run wisp upgrade.
    - security 1.0.0-pre.3: Signed cookies under WISP_BASE keep their name and base path
    - 1.0.0-pre.3: Brotli for assets and baked pages, assets from other sites by URL
    Changes: https://wispweb.dev/changelog/?v=1.0.0-pre.2

Under the line it lists at most five lines on what changed between the CLI and the newest release, taken from the release notes (the section of CHANGELOG.md that the release carries): the first lines of each Security section, then the summary of each newer version. The notes are cached with the answer. When they cannot be read the notice is the one line. wisp upgrade prints the same lines before it installs, and wisp changelog --since 1.0.0-pre.2 prints every entry after that version. wisp changelog alone prints the installed version and everything newer; wisp changelog 1.0.0-pre.2 prints one version. Versions up to the installed one come from the CHANGELOG.md built into the CLI, so they work offline; newer ones are read from the releases at code.wispweb.dev, and without a network the command says so and shows what it has. The link opens the changelog page with your version marked.

The lookup runs on a background thread with a 3 second limit. A command never waits for it, and the server and the built app never run it. It is off when offline, when CI is set, and with WISP_NO_UPDATE_CHECK=1. On Windows the running wisp.exe is renamed to wisp-old.exe for the install.

CLI Older Than the App

App commands warn on stderr when the installed wisp is older than the app's wisp crate, and ask before continuing in a terminal. WISP_NO_UPDATE_CHECK=1 turns the warning off. More: CLI, dev loop and security.

Is This Page Useful?

View This Page's Source

Type to search every docs page and heading.