Local and LAN security
Français · Type: explanation + reference · Audience: author · Context: local Studio · Status: ready · Applies from: 4.2.1
The local Studio · contract: stable · verified by CI (support matrix of 4.4.0, 2026-10-23): Chromium end to end (the Studio's checks); Windows: the unit suite only · automated checks: jobs
e2e,windows; CI plane2e,windows; 2 test files · people's passes:studio-hero-matrixnot run (#238) · known limits:lan-is-not-public
The local Studio writes files on your machine and can call a model provider with your key. This page says what protects those routes, on your own machine and on your local network, and where the protection stops.
Why
A dev server that writes files is a door into your machine. Three things could knock on it: another machine on the
network, a web page in your browser that tries to reach localhost (directly, or by rebinding its own name to your
machine, "DNS rebinding"), and a model provider address that would lead the Studio to a service of your private
network ("SSRF"). The Studio closes each one by default and opens only what you ask for.
Local and LAN
- Loopback by default.
npm run studioandnpm run devlisten on127.0.0.1(port 5173): another machine cannot reach their file-writing routes. - Its own names only. Every Studio, asset, layout and assistant route answers only to the names of this machine
(
localhost,127.0.0.1,[::1]), and in LAN mode to the addressesstudio:lanserves. A page on another site that rebinds its name to your machine sends that name asHost: refused (403), even with a matchingOrigin. - Same origin. A request that carries an
Originmust come from the Studio's own origin (403 otherwise). A write (anything but GET, HEAD and OPTIONS) without anOriginheader is refused (403): a script must send one. - LAN mode, behind a token. To work from a phone,
npm run studio:lan(ornpm run dev:lan) listens on the network and prints a fresh capability token, drawn at each start (24 random bytes), unless you setWEB_SCUMM_STUDIO_TOKENyourself. Every Studio, asset, layout and assistant route then requires that token, as thex-web-scumm-tokenheader, the?token=parameter of the printed URL, or the cookie the URL sets (web_scumm_studio,HttpOnly,SameSite=Strict). The host names allowed are the machine's IPv4 network addresses (WEB_SCUMM_STUDIO_HOSTS, set by the command). - Never on the Internet. Do not expose the development port to the Internet: the token protects a session on your network, not a public server. To put a Studio online, use the hosted Studio (What runs where).
Guarantee ·
lan-token-same-origin· referenced · executed onaff6caab7514· scope self-checked by the author (AI), not reviewed In LAN mode the Studio answers 401 to a request without the session's token, and 403 to a write whoseOriginis another site, even with the token.
Guarantee ·
own-names-only· referenced · executed onaff6caab7514· scope self-checked by the author (AI), not reviewed AHostthat is not this machine (or an addressstudio:lanserves) is refused, even with a matchingOrigin.
Model providers
- Keys stay in the browser tab (sessionStorage) and go with each request; the relay keeps a key in memory for that request only, never on disk, never in a log (The Assistant).
- Addresses. Arbitrary provider URLs are disabled unless
WEB_SCUMM_ALLOW_CUSTOM_PROVIDER=1. Even then, private and link-local destinations stay blocked, except the explicit local Ollama preset, in both IP families: loopback (127.0.0.0/8,[::1]), unspecified (0.0.0.0,::), private (10/8,172.16/12,192.168/16,100.64/10,fc00::/7), link-local (169.254/16,fe80::/10), IPv4-mapped IPv6, and the nameslocalhost,*.localhost,*.local,*.internal. A custom provider must use HTTPS. - Calls. A provider call never follows a redirect, gives up after 60 s, and reads at most 8 MB of answer.
What is not protected: the game is trusted code
A game's TypeScript and its custom commands are trusted code, not sandboxed data: the Studio, the validator and the
solver run them on your machine. Open only games you trust, as you would any project you npm install. See
SECURITY.md.
Limit ·
lan-is-not-publicThe LAN token is a convenience for a phone on your own network: it is shown in the terminal and in the URL, and lives as long as the server. It is not an authentication for a public deployment.
Alternatives set aside
- Listening on every interface by default: convenient for phones, but it would put the file-writing routes on every network the laptop joins. LAN mode is an explicit command.
- A password: a token drawn per session needs nothing to remember and dies with the server.
- Allowing any provider URL: one setting away (
WEB_SCUMM_ALLOW_CUSTOM_PROVIDER=1), but a URL typed in a page must never reach your private network.
Trade-offs
- A script that writes through the Studio's API must send an
Originheader matching the Studio, and in LAN mode the token. - A phone needs the URL with the token once; the cookie keeps it for the session.
Decisions and ADRs
- Sources:
tools/studio/security.ts,tools/serve-args.ts,tools/studio/assistant.ts,vite.config.ts. - The variables:
WEB_SCUMM_STUDIO_TOKEN,WEB_SCUMM_STUDIO_HOSTS,WEB_SCUMM_LAN,WEB_SCUMM_ALLOW_CUSTOM_PROVIDER. - When a route refuses: Studio troubleshooting.