v0.1.11 — Upload without base64 (ticket flow)
Upload a receipt without pushing its bytes through the model context. Based on the contribution by @gutencoder (#34, merged as #38); the server-side URL-fetch tool from that PR was intentionally held back (see the CHANGELOG Security note).
Added
create-upload-ticket/get-upload-result(drafts tier): a short-lived (15 min), single-use ticket with a browser drag-and-drop URL and a ready-to-runcurlcommand. Bytes go client → server → Lexware; the model only sees the file id. Filenames travel asX-Filename-B64(base64url of the UTF-8 bytes), so umlauts, dashes, quotes and emoji survive.SERVER_URL(orOAUTH_RESOURCE) now applies in every auth mode — upload links are built from it; static-token deployments behind a real domain no longer hand out loopback links.
Security & hardening
- The
/upload/:ticketroute mounts only when the drafts capability is enabled; the ticket page is servedno-store+nosniff+ frame-denied. - One buffering body per ticket at a time; invalid/expired/used tickets are rejected before any body is read; gzip framing refused (
inflate: false). - Operator credential failures (Lexware 401/403) answered as generic 502 to the unauthenticated uploader; unknown-outcome transport failures name the duplicate risk.
upload-file-from-urldeferred: its DNS-rebinding TOCTOU is moot for the built-in Microsoft allow-list but live for custom allow-lists; it returns only with connection-level IP pinning, disabled by default.
Notes
- The ticket store is in-process — a single-instance feature (documented in the CHANGELOG).
- 250 tests, including an end-to-end test that executes the emitted
curlcommand against the live routes.
Full details in CHANGELOG.md.