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

Wisp

Environment Variables

Wisp reads these once at start. An empty value counts as unset. A non-empty invalid value stops the server with a message. Switches take on/off (also 1/0, true/false).

Browser code reads PUBLIC_* variables as env.PUBLIC_X, filled in at build. With public_env = true in [package.metadata.wisp], import { env } from 'wisp' reads them as the server saw them at its start (Config). A prerendered page (PRERENDER) that the server sends holds the server's values from its start. wisp build --static runs the server on the build machine, so the exported pages hold that machine's values.

A variable comes from the process first. Then Wisp reads these files in the app folder. A later file wins: .env, then .env.development (dev mode, WISP_DEV) or .env.production (other modes), then .env.local, then .env.development.local or .env.production.local. A file that does not exist adds nothing. Wisp reads the files once, at start. The build reads the files of its profile (release reads production) for the baked PUBLIC_* values, and the server picks its mode from WISP_DEV at start. Set WISP_DEV in the process or in .env: the mode files cannot choose the mode. The mode follows the build profile: a debug build (as under wisp dev) reads development and a release build reads production, unless WISP_DEV says otherwise. Cargo does not see a mode file that you create after the first build. Run cargo clean -p <app> and then wisp build again, or touch build.rs. Keep the .local files out of git (wisp new ignores .env.*).

For your own variables use wisp::env("KEY"), wisp::env_or("KEY", 4) or #[derive(Config)]. Call Conf::get() after the app starts, or Conf::load()? first in a test or another binary, because get() panics on bad variables.

Server

NameWhat it does
HOST, PORTlisten address (0.0.0.0:3000 in release, 127.0.0.1 in dev)
ORIGINthe site's address as browsers see it, for a proxy that does not pass Host. A .onion address turns WISP_ONION on
WISP_DEVdev mode: on in debug builds. A release binary with WISP_DEV reads static/ and .wisp/ in its working directory, so run it from the app folder
WISP_THREADSworker threads, one per CPU
WISP_LISTENunix:<path>: listen on that Unix-domain socket instead of HOST/PORT, for a proxy on the same machine (Deploy). Linux and macOS, on tokio's driver (not io_uring or epoll). A stale socket file there is replaced; a live server's socket and any other file are an error, and the file is removed on stop. WebSocket upgrades fail on it. Any other value stops the server
WISP_IOLinux: epoll instead of io_uring; uring fails at start where it cannot run
WISP_MAX_CONNSopen connections before a 503 (unset: on Linux the open-file limit less 1024, within a quarter of memory at 16 KiB each; 10000 elsewhere; 0 no cap)
WISP_BODY_LIMITlargest request body (512KB, 10MB; 1 MB). const BODY_LIMIT sets it per route
WISP_UPLOAD_LIMITmost a request's Upload files may take in all (100 MB)
WISP_SPOOL_LIMITmost all requests together may stream to upload temp files at once, process-wide (default: 16× WISP_UPLOAD_LIMIT). Over it, a new file part is refused 503 until space frees; 0 no cap
WISP_HANDLER_TIMEOUThandler timeout in seconds
WISP_WS_IDLEseconds a quiet WebSocket lives (60; 0 never)
WISP_SSE_HEARTBEATseconds between : comment lines on a quiet Response::events stream (25; 0 none)
WISP_DRAINseconds a stopping server waits for requests under way (10)
WISP_STOP_DELAYseconds between SIGTERM and closing the listener, while /_wisp/health answers 503 (0)
WISP_USERuid or uid:gid: started as root, give WISP_DATA to that user and run as them. If the server cannot start itself as that user, it stops and does not run as root
LISTEN_FDSLinux systemd socket activation: with LISTEN_PID this process, serve on the socket at descriptor 3 instead of binding PORT
LISTEN_PIDthe process LISTEN_FDS is meant for, set by systemd
WISP_CLIENT_IP_DEPTHwhich entry of WISP_CLIENT_IP_HEADER (and of x-forwarded-proto, x-forwarded-host), counted from the right, is the client's (1). Use 2 behind Render or Cloud Run
WISP_CLIENT_IP_HEADERheader with the client address behind a proxy. The last address of the header's last line counts, the one the trusted proxy added; a client can forge the first. Setting it also makes x-forwarded-proto: http believed (plain cookies); unset, cookies are Secure except in dev and for plain http to localhost or loopback. wisp deploy init sets it where the host's header is safe
WISP_LAMBDA_STREAM1 on AWS Lambda with a Function URL in RESPONSE_STREAM mode (Streaming)
WISP_SECURE_HEADERSoff leaves out nosniff and referrer-policy
WISP_REFERRERthe referrer-policy pages get (default strict-origin-when-cross-origin)
WISP_HSTSon adds strict-transport-security, not on plain http requests
WISP_ONIONon: serve as a Tor onion service. Automatic for a .onion ORIGIN or SITE_URL; off turns it off; auto counts only requests to a .onion host. See Tor onion services
WISP_ONION_LOCATIONan onion address: clearnet pages answer with onion-location: http://….onion/<path>
WISP_SERVER_TIMINGServer-Timing on every answer (on in dev)
WISP_PROBLEM_JSONon: errors as RFC 9457 problem JSON
WISP_REQUEST_IDon: every request gets an id (cx.request_id()), echoed as x-request-id
WISP_ALLOWED_HOSTScomma-separated Host values (127.0.0.1:1420,localhost:1420, with the port the browser sends; on 80 or 443 list it without the port, as browsers leave it out). A request with another Host, or with none (HTTP/1.0), is a 421 (Misdirected Request), so a page on another name pointed at this address (DNS rebinding) cannot read the app. Unset: any host, and nothing is checked. Not derived from ORIGIN, since a proxy may rewrite Host. wisp::allow_hosts sets it from code (Deploy targets)
WISP_API_DOCSserve /_wisp/openapi.json and /_wisp/docs (on in dev; no docs page on Cloudflare, Pages, Vercel, Netlify)
WISP_SECRET, WISP_SECRET_OLDkey for signed cookies and tokens, and the one before (rotation). Unset outside dev, an app that signs does not start (openssl rand -hex 32)
WISP_DATAfolder for table files (off: in memory)
WISP_FSYNCsecond (default), always, off
WISP_STOREa store for tables: d1:DB, deno-kv, libsql://…
WISP_KV_<NAME>driver of wisp::kv("NAME"): cf:BINDING, deno-kv[:path], netlify-blobs[:store], fs[:dir], memory, upstash[:url]. Unset: the host's own store. See Storage
WISP_BLOB_<NAME>the same for wisp::blobs("NAME"), plus s3:<endpoint>/<bucket> and vercel-blob[:private]
WISP_SQL_<NAME>database of wisp::sql("NAME"): libsql://<host>, https://<host> (Turso), d1-http:<account>/<database>. Unset: the D1 binding on Cloudflare, else 501
WISP_KV_<NAME>_TOKEN, WISP_SQL_<NAME>_TOKEN, WISP_BLOB_<NAME>_TOKENthe token for that store's driver
WISP_BLOB_<NAME>_KEY_ID, WISP_BLOB_<NAME>_SECRET, WISP_BLOB_<NAME>_REGION, WISP_BLOB_<NAME>_SESSION_TOKENthe s3: driver's credentials (else AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_SESSION_TOKEN)
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN, KV_REST_API_URL, KV_REST_API_TOKENREST address and token for upstash
BLOB_READ_WRITE_TOKEN, VERCEL_BLOB_API_URLtoken and API address for vercel-blob
CLOUDFLARE_API_TOKENtoken for d1-http: when WISP_SQL_<NAME>_TOKEN is unset
NETLIFY_BLOBS_CONTEXTset by Netlify, for netlify-blobs
WISP_STORE_TOKENbearer token for a libsql:// store
WISP_STORE_POLLseconds between store change polls (several servers)
WISP_BASEbase path, set at build (/app); same as base in [package.metadata.wisp]
WISP_ASSET_BASEwhere built /_app/ files are linked from, set at build (https://cdn.example.com or /static). A cross-origin host is added to script-src and style-src of the default CSP
SITE_URLsite address for sitemaps, feeds and absolute links (also read from the .env files). Without it, only dev mode takes the request's Host. A release build then serves no /sitemap.xml, /robots.txt or /feed.xml, and wisp::alternates gives nothing. An edge build always uses SITE_URL alone, so set it on the edge host.
SITE_TITLEfeed title
METRICS_KEYbearer key for /_wisp/metrics
CRON_SECRETbearer key hosts send to /_wisp/cron/<schedule>
WISP_SERVE1: Node's server.mjs listens even when a launcher imports it (PM2 cluster, symlinked path)
WISP_EXIT_ON_STDIN_END1: Node, Bun and Deno servers exit when stdin closes (wisp dev --target sets it)
WISP_NODE_HTTP1: Node, Bun and Deno targets use the runtime's own HTTP server
WISP_KEEP_ALIVE_TIMEOUTNode, Bun and Deno targets: seconds a quiet kept-alive connection lives (65; Bun.serve takes at most 255)
AWS_LAMBDA_RUNTIME_APIset by Lambda; the wisp build --target lambda binary answers its runtime API
AWS_ENDPOINT_URL_APIGATEWAYMANAGEMENTAPIwhere a Lambda's WebSocket sends go (a VPC endpoint, LocalStack, a test)
WISP_EDITOR, EDITORwhat the dev error dialog's Open runs (code -g)

#[derive(Rest)] bearer tokens and OAuth client ids and secrets are read from the names their attributes give.

Set by the CLI and Hosts

You do not set these.

NameWhat it does
WISP_NONCE0 while wisp build builds wasm for an app with no wisp::csp_nonce(, leaving the nonce code out
WISP_REQUEST_ONLY1 for Cloudflare, Pages, Vercel or Netlify builds: leaves the raw-connection server loop out of the wasm
WISP_LITS1 in wisp dev cargo builds: string, number and bool literals are read from a table so a literal-only save swaps in with no compile (see Literal Swap)
WISP_WARM_UPthe Cloudflare worker's warm-up instance (WISP_WARM_UP=1)

WISP_NONCE=0: 6,747 bytes (about 6.7 KB) smaller for wisp-test-app (2,527,786 bytes without it, 2,521,039 with it, Windows host, bench/edge/README.md).

Logs and Traces

NameWhat it does
WISP_LOGjson: a JSON line per request with a severity
OTEL_EXPORTER_OTLP_ENDPOINTOTLP/HTTP traces to <endpoint>/v1/traces
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTthe traces URL, as is
OTEL_EXPORTER_OTLP_HEADERSapi-key=…,x=…
OTEL_EXPORTER_OTLP_TRACES_HEADERSthe same, for traces only (wins)
OTEL_SERVICE_NAMEservice name on spans
OTEL_BSP_SCHEDULE_DELAYbatch delay in ms (5000)

CLI and Tests

NameWhat it does
SSL_CERT_FILEwisp dev only: a PEM file of extra root certificates for the https:// targets of [package.metadata.wisp.dev.proxy], such as a local server with its own certificate (.wisp/dev-cert.pem of another wisp dev --https)
WISP_DEV_ORIGINextra hosts, comma-separated, that may read wisp dev's event stream (Security)
WISP_STATIC1 at build: keep the static export (WISP_EXPORT). wisp build --static sets it. A release build with no PRERENDER page leaves the prerenderer out, and WISP_PRERENDER then writes nothing
WISP_LAMBDA1 at build: keep the AWS Lambda runtime. wisp build --target lambda sets it
WISP_DEAD_LINKSwarn: wisp build (its prerender crawl) and wisp build --static only warn of dead links and missing #ids, like --allow-dead-links (Edge and serverless targets)
WISP_NO_CRAWL1: wisp build (its prerender) and wisp build --static follow no link, like --no-crawl
WISP_NO_UPDATE_CHECK1 silences the CLI-older-than-app warning and the daily newer-release lookup and the changes it lists (CI set does the same for the lookup)
WISP_RELEASES_URLthe https release list URL that wisp upgrade, wisp changelog and the newer-release lookup read (default: the Forgejo API at code.wispweb.dev). Forgejo or GitHub releases JSON
WISP_PORT_TRIEShow many next ports wisp dev tries (20)
WISP_DEV_CARGOany value: wisp dev builds with cargo, never rustc directly
WISP_BUILD_CACHE0: off. A path: where the build cache lives. By default wisp build, dev, test and new share one cargo build dir (%LOCALAPPDATA%\wisp\build, ~/Library/Caches/wisp/build, ~/.cache/wisp/build), so a second app reuses compiled dependencies. Skipped when CARGO_TARGET_DIR, CARGO_BUILD_TARGET_DIR, CARGO_BUILD_BUILD_DIR or a cargo config target-dir/build-dir is set. On E0463, E0786, E0460, E0514 or LNK1207 it cleans that package and retries once. wisp clean --cache empties it
WISP_DEV_DIRECT1: on macOS, wisp dev builds .rs and .wisp saves with rustc directly (experimental); on Linux it skips the startup proof
WISP_DEV_PORTset by wisp dev for [package.metadata.wisp.dev] run commands: the port it serves on (Config)
WISP_CWEBPthe cwebp wisp build uses for images
WISP_SASSthe Dart Sass for src/app.scss
WISP_TSCthe tsc for wisp check --types
WISP_BROWSERChrome or Edge for browser tests
NO_COLORplain CLI output

Is This Page Useful?

View This Page's Source

Type to search every docs page and heading.