Security and input bounds

vibe-sketch runs as a local server that the browser shell, the desktop window and agents talk to, and it reads files that come from elsewhere: a co-author’s ChemDraw file, a QVF from another program, a SMILES list from a database. This page records what it trusts, what it bounds and what is still open. It is the first part of the 0.8 reliability and security qualification (issue #69). That qualification as a whole is not complete, and no support claim rests on this page.

What is trusted

Party

Trusted with

Why

The user at this machine

Everything the server can do, including writing files they name

It is their sketcher.

Agents the user starts (vibe-sketch mcp)

The same, through the REST API

They act for the user.

Web pages in the user’s browser

Nothing

Any site can make the browser send requests to 127.0.0.1.

Files and pasted data

Their contents only

They are read, never run.

Other machines on the network

Nothing, unless the user binds to a network interface

The default bind is 127.0.0.1.

The local server

vibe-sketch serve and the desktop window bind to 127.0.0.1 by default. Such a loopback server:

  • answers only requests whose Host is a name of this machine (127.0.0.1, localhost, ::1), and refuses others with 421. A site that points its own name at 127.0.0.1 (DNS rebinding) shares the server’s origin in the browser, but it still sends its own name.

  • refuses a request whose Origin is another site’s, or null, with 403. The browser shell, the desktop window and a development server on localhost are on this machine and pass.

A server bound to another interface (--host 0.0.0.0) was asked for by name. It takes any Host, prints that anyone who can reach it can use the API, and leaves access control to the network. It has no login.

Every request body is read up to 272 MiB (the largest QVF in base64, with room to spare) before the application parses it; a longer one answers 413.

What a file may hold

Each check runs before the work it protects, and a refusal says which bound was met (vibesketch.limits):

Input

Bound

Protects

An imported file or text, a .vsk document

64 MiB

parsing

A QVF archive

192 MiB packed; 10,000 files; 64 MiB for a JSON member and 1 GiB for any other member, unpacked; 2 GiB in all

memory: a small archive that unpacks to gigabytes (a zip bomb) is refused from its directory, before anything is unpacked

JSON (KET, .vsk)

64 levels of nesting

the parser and the validator; JSON too deep for the parser itself is refused too

One import

25,000 atoms

derivation of species and reactions

A structure without 2D coordinates (SMILES, InChI, a name, XYZ)

2,500 atoms

2D layout, which grows faster than linearly: 5,000 atoms take about 17 s; a drawn structure of that size, with coordinates, imports

CDXML

expat’s limits on entity expansion

memory: the “billion laughs” document is refused by the XML parser

A campaign

2,000 nodes (two per species and stage); 4,096 cpus and 16 TiB of memory per node

disk: every node is a directory of files written before the queue sees it

A canvas that is not one, such as KET whose nodes are not a list, is refused as an invalid canvas instead of failing inside the server.

Files are read, never executed: formats are parsed into data, and no imported file names a program to run. The routes that take a filesystem path (campaign and render directories) and Save, which writes to the file the server opened, act for whoever passes the checks above: the user and their agents.

Saving

Every save writes a hidden temporary file beside the target, makes it reach the disk, renames it over the target and then syncs the directory. A crash, a full disk or an interrupted save therefore leaves either the old file or the new one, never a mixture. A crash at the wrong moment can leave a hidden .NAME.*.tmp file beside the document; it is safe to delete.

A document the server opened from a file (vibe-sketch serve FILE, the browser’s Save) remembers the file’s bytes as it read or last wrote them. If another program changed, replaced, deleted or created the file since, Save is refused with 409 and the file is left as the other program wrote it. The edit stays in the open document: reopen the file to see the other change, or keep this version with a download under another name. The desktop window checks the same before its Save. A stale revision (two clients editing at once) answers 409 with the current revision, and the client re-reads; missing optional programs answer with an installation hint instead of failing.

Directories

Compiling a campaign and rendering its stages write into a directory the client names. It must be absolute, and new or empty; a directory with anything in it is never touched. If the write fails (no permission, a full disk), the refusal says why. A directory that was made for the write is removed, and one the client gave is left empty, as it was. The paths a campaign’s own files and records hold may come from a document someone else wrote, so a render refuses any that lead outside the campaign, and a campaign whose files are missing or unreadable is refused with the reason.

A drawing sent over REST (PUT /api/documents/{id}/canvas) and a posted document meet the same bounds as an import: nesting, atoms, and a canvas that is one.

Updating

scripts/update.sh only fast-forwards the checkout, so an interrupted fetch leaves it as it was. With --recreate-venv, the previous virtualenv is set aside as VENV.previous until the new one is installed and answers; if the update fails or is interrupted (Ctrl-C included), the new one is removed and the previous one restored. A VENV.previous left behind by a killed update stops the next update with the way out: move it back or delete it. An update without --recreate-venv reinstalls into the existing virtualenv, where pip replaces one package at a time; run the update again if it was interrupted.

Desktop startup

The desktop window starts its own backend and waits for it. Only the backend’s own answer counts as ready (200 {"status": "ok"} from /health), so another program on the port, or an error page, never opens the editor. A backend that dies while starting ends the wait at once and the setup window says it did not start, with its exit status in the log. A backend that stops later closes the editor window and shows The vibe-sketch server stopped, with Try again to start a new one; changes since the last save were held by the stopped backend.

Still open

These parts of #69 are not done yet:

  • Clean installation and packaged flows on each supported platform.