v1.37.0 — vecline serve: Office conversion without uploading
A browser tab has no office engine. The two usual ways to give it one each cost something this project is built on: a ~300 MB LibreOffice-in-WASM destroys instant load, and uploading the document destroys the privacy claim outright.
So neither. vecline serve runs a tiny loopback endpoint, and Vecline Studio hands the document to your machine:
npm install -g vecline && vecline serveEvery link stays local:
.docx → your LibreOffice → PDF → mupdf in the tab → pixels → traced SVG
Two endpoints, both taking bytes and returning bytes. No request field anywhere names a file, so there is nothing to traverse or overwrite.
Built to be attacked, then actually attacked
Four agents worked distinct threat classes — hostile web page, input handling, auth, and the LibreOffice boundary — and every claimed finding was independently re-tested against a live server before being believed. No breached boundary. Four hardening gaps, all fixed before release:
- A failed conversion returned LibreOffice's raw error, carrying both temp directory layouts, the resolved
sofficepath and hence the OS account name — script-readable by an allowlisted origin. Only messages written for a person are sent now; the detail stays on your console. - Any loopback origin on any port was allowed, so every other page served from localhost had full bridge access. Now only vecline.xyz and origins named with
--allow-origin. - Concurrency was unbounded — six concurrent 64 MB bodies took memory from 100 MB to 722 MB. Capped at two, which costs nothing: LibreOffice serialises on one user profile anyway.
--token=silently started with authentication off. It now refuses, and--token-file/VECLINE_TOKENtake precedence over the flag, because an argument is readable from the process table by exactly the account a token exists to fence out.
The threat model in the source header was corrected in four places where the review showed it overclaimed — most importantly "nothing to exfiltrate", which the error text falsified.
Two bugs the tests caught that reading would not have
- The oversize-body path answered mid-upload, racing the still-arriving body. curl coped;
fetchreportedECONNRESETand never saw the 413. Past the cap the body is now drained rather than kept. - The client-disconnect check tested
req.destroyed, which is also true on the happy path once a body is consumed — it rejected every legitimate request until the empty-document test went red.
500 tests · smoke clean · corpus problems: 0
vecline.mcpb is attached — open it with Claude Desktop to install the MCP server in one click.