Skip to content

Quilltap 3.0.0

Choose a tag to compare

@csebold csebold released this 01 Apr 06:51
· 2554 commits to main since this release
3.0.0

Quilltap v3.0.0 Release Notes

The Estate has outgrown its browser. It now arrives at your door.


There is a particular kind of morning — the kind with good coffee and a draft that won't stop arriving — when one looks at a perfectly serviceable web application and says, "What if this lived on the desktop, sandboxed inside a virtual machine, and also ran on Windows?" This is, broadly speaking, what happened. What you are reading is the consequence.

Quilltap 3.0 is, to put it as simply as the thing will allow, a native (-ish) desktop application. Where once a Docker container sat politely at localhost:3000 waiting for someone to visit, Quilltap now installs itself as a proper citizen of your operating system: a macOS .dmg, a Windows installer, or a Linux .deb / AppImage, each containing an Electron shell wrapped around a virtual machine that runs the entire backend in comfortable isolation. Your data stays on your machine. Your AI-generated code runs in a sandbox. The architecture is, if we are being charitable, ambitious; if we are being accurate, the sort of thing that causes build engineers to develop a faint twitch behind one eye.

It was worth it.

What Changed (A Brief Précis for the Impatient)

The entire infrastructure was rewritten. The authentication system was removed. The Docker deployment was simplified from a multi-service orchestration to a single command. A native desktop application was built from scratch for macOS, Windows, and Linux. The settings were reorganized. The images were optimized by 94%. The database was hardened. Two new subsystem characters were introduced. The file storage was stripped to its essentials. And the virtual machine — patient, deterministic, sandboxed — was made the foundation of the whole affair.

If you are upgrading from 2.x, you may feel, briefly, as though you have wandered into a different house. You have. But the furniture is arranged the same way, and the staff — Prospero, Aurora, Calliope, and the rest — are all present, merely better dressed.


The Desktop Application

The Electron Shell

The headline, naturally, is that Quilltap is now a desktop application. On macOS, it runs atop a Lima virtual machine using Apple's Virtualization.framework. On Windows, it runs inside WSL2. On Linux, it uses Docker Engine directly, because Linux was already the sandbox and always knew it.

The architecture follows a simple pattern: Electron provides the window, the menu bar, and the system tray. A virtual machine (or container) runs the full Next.js backend. The two communicate over localhost. The user sees a native application window. The application sees a Linux server. Everyone is happy, except perhaps the build pipeline, which is not consulted on matters of happiness.

The Splash Screen

First impressions matter, and Quilltap's splash screen has been designed accordingly — an Art Deco–framed loading sequence with animated progress, live VM output streaming, and color-coded log levels. First-run users see a branded experience with Quilltap's quill logo and serif typography. Subsequent launches auto-start with the last-used data directory, offering a brief window to interrupt and choose another.

When the application shuts down, a small frameless window appears with a spinner and a polite notice — "Stopping virtual machine…" — while the backend is dismantled behind the scenes. No orphaned processes. No dangling containers. Just a clean exit, like a well-run household.

Data Directory Management

Each Quilltap site now lives in its own named data directory, selectable from the splash screen. Users can maintain multiple sites — one for work, one for fiction, one for experiments — and switch between them without quitting. Directories display human-readable names (editable via a pencil icon), disk usage, and VM size. Deleting a directory offers two options: remove the configuration only, or delete the data along with it.

On macOS, each directory receives its own Lima VM instance (quilttap-<hash>), so switching is a stop-and-start operation rather than a destructive rebuild.

Runtime Modes

The splash screen offers a toggle between two runtime backends:

  • VM Mode — Lima on macOS, WSL2 on Windows. Full isolation. The Foundryman's preferred arrangement.
  • Docker Mode — Available on all platforms. Uses Docker Engine directly, with the same container image that powers the standalone deployment.

Port conflicts are handled automatically: starting one mode stops the other. The active runtime is displayed in the application footer, alongside the data directory path (now clickable — Electron opens your file browser; browser mode copies to clipboard).


Docker, Simplified

The Docker story has been rewritten with a controlled demolition's precision. Removed: docker-compose.yml (all three of them), the Nginx configuration, the MinIO buckets, the authentication scaffolding, the Let's Encrypt scripts, and the OAuth infrastructure. In their place: a single docker run command, a pair of platform-aware startup scripts, and transparent host port forwarding via socat.

One Command to Start

./scripts/start-quilttap.sh

That's it. The script detects your platform, sets the correct data directory, finds Ollama if it's running, and starts the container. Windows users have start-quilttap.ps1. Both scripts support --data-dir, --port, --redirect-ports, --dry-run, and --no-auto-detect.

Host Service Access

Docker users can now reach host services — Ollama, LM Studio, MCP servers — at localhost URLs from inside the container, without --network host or manual IP discovery. The HOST_REDIRECT_PORTS environment variable accepts a comma-separated list of ports, and socat forwarders handle the rest. The startup scripts auto-detect Ollama on port 11434 and configure this transparently.

Localhost URL Rewriting

A new rewriteLocalhostUrl() function transparently rewrites localhost and 127.0.0.1 URLs to the host gateway IP in Docker, Lima, and WSL2 environments. The function auto-detects the gateway via host.docker.internal or the default route, and supports a QUILTTAP_HOST_IP override. This replaces the socat-based approach for LLM provider connections and is applied to all provider creation, embedding profiles, image profiles, and MCP server connections.


Authentication: Removed

The authentication system — JWT tokens, OAuth flows, Google sign-in, app-specific passwords — has been removed entirely. Quilltap is a local-first application. Your data lives on your machine. There is no server to authenticate against, because you are the server. The Pepper Vault handles encryption key management for sensitive data at rest. No login required.


Settings Reorganization

The ten-page Foundry subsystem navigation has been replaced with a single tabbed settings page using seven plain-English tabs: AI Providers, Chat, Appearance, Memory & Search, Images, Templates & Prompts, and Data & System. The estate personifications remain as thumbnails and flavor text — they are, after all, part of the charm — but they no longer gate navigation.

All old /foundry/* routes redirect to the appropriate tab. The AI Stack Setup Wizard has been relocated to /settings/wizard.

The Setup Wizard

A new guided six-step wizard walks first-time users through provider configuration: provider selection, API key validation, model selection, optional embedding and image setup, and a final test-and-confirm step. It pre-populates existing configuration when accessed from settings. One flow creates connection profiles, embedding profiles, image profiles, and chat settings in a single pass.


New Faces on the Estate

Pascal the Croupier

Downstairs, past the Library and the Salon, lies a narrow room lit by green lamps. Pascal keeps the dice and the records there. He is the subsystem responsible for games, randomness, and state management — the quiet master of probability and consequence. His page lives at /foundry/pascal, with a dice icon and a coming-soon notice that suggests patience.

Saquel Ytzama, Keeper of Secrets

Saquel manages encryption and security — the Pepper Vault, key rotation, and the quiet business of keeping things that should be locked, locked. Her page lives at /foundry/saquel, marked with a key icon and a similar promise of things to come.


Database & Storage

SQLite Hardening

The database has been fortified with the quiet thoroughness of a butler who has survived two world wars:

  • Integrity checks on startup (PRAGMA quick_check)
  • Periodic WAL checkpoints every five minutes
  • Physical backups on startup, with tiered retention: daily for seven days, weekly for four weeks, monthly for twelve months, yearly forever
  • synchronous = FULL for durable writes
  • Pre-backup WAL flush for logical backups
  • Crash-loop protection in Electron: after three consecutive startup failures, safe mode engages — clearing caches, resetting saved state, and restoring defaults

Vector Embedding Optimization

Embeddings in vector_entries and memories.embedding are now stored as compact Float32 BLOBs — roughly 4–5× smaller than the previous JSON text representation. A new vector_entries table with one row per embedding replaces the monolithic entries JSON column. Incremental saves write only changed entries. Migration converts existing data automatically.

File Storage Simplification

The file storage abstraction has been reduced to a thin local-only FileStorageManager. Removed: the mount_points table, S3 support, the S3 plugin, the mount-points API, the storage settings UI, and approximately thirty consumer files that referenced the old abstraction. File paths are now portable across platforms, with runtime-resolved paths replacing database-stored absolute paths.


Build & Release Pipeline

Automated Releases

The release workflow, triggered on version tags, now builds everything in parallel: rootfs tarballs (amd64 + arm64), Electron installers for macOS (DMG), Windows (NSIS), and Linux (AppImage + .deb), Docker images, and standalone tarballs. A final job creates the GitHub Release with all assets attached.

Noteworthy build improvements:

  • Azure Trusted Signing for Windows code signing
  • App Store Connect API key for macOS notarization (replacing Apple ID + app-specific password)
  • Native architecture runners for rootfs builds — no more QEMU cross-compilation
  • Image optimization — all PNGs and JPGs converted to WebP, SVGs optimized with SVGO. Total image payload reduced from ~75 MB to ~4.6 MB (94% savings)
  • node_modules excluded from Electron app.asar — the Electron code uses zero npm dependencies, so the 502 MB node_modules was bundled for nothing. DMG size dropped from 262 MB to ~132 MB
  • Plugin node_modules stripped from Docker image — saving ~350 MB per architecture

The quilttap npm Package

For those who prefer the command line:

npx quilttap

The npm package is now a lightweight CLI (~10 KB) that downloads pre-built standalone output from GitHub Releases on first run. Downloads are cached per-version in a platform-specific directory, with a progress bar, retry with exponential backoff, and an --update flag for forced re-download.

Lima Distribution

Lima binaries are now downloaded directly from GitHub Releases at first launch, with local caching. No Homebrew installation required. A runtime check for Xcode Command Line Tools offers one-click installation if missing. Lima version is pinned in electron/constants.ts.


MCP & Tool Connectivity

MCP connections in Docker, Lima, and WSL2 environments received extensive attention in this release, which is a polite way of saying they were broken in several interesting ways and are now fixed:

  • Host header validation — MCP servers rejected requests from host.docker.internal. A custom fetch implementation now routes traffic through the host gateway while preserving the original Host: localhost:PORT header.
  • Connection churn — URL preprocessing caused config hash mismatches between raw and rewritten URLs, causing the MCP plugin to teardown and recreate connections on every API call. URL rewriting now happens solely in MCPClient.connect().
  • Gateway resolution — Docker environments now use host.docker.internal first (via Docker Desktop DNS), with /proc/net/route reserved for Lima/WSL2 where NAT networking forwards to host loopback.

The allowToolUse master switch on connection profiles provides a profile-level override for all LLM tools. When disabled, no tools are sent to the model regardless of other settings.


Performance & Stability

  • Node.js OOM protectionNODE_OPTIONS="--max-old-space-size=2048" set in all runtime environments; Lima VM memory bumped from 2 GiB to 4 GiB
  • Backup/restore rewritten for streaming — disk-based temp files with shell zip/unzip replace in-memory archiver/adm-zip, fixing OOM crashes on large data directories
  • Large .qtap import — files over 10 MB no longer choke; proxy body limit raised to 100 MB and the upload pipeline corrected
  • Electron downloads — all downloads (backups, exports, images, API keys) now work via preload bridge IPC channels that stream to disk without memory pressure
  • Infinite re-render loop — an unstable useEffect dependency in the Salon page caused an infinite loop that stalled React's startTransition, making all sidebar navigation silently fail. Fixed with stable dependency proxies
  • Lima VM startup noise — suppressed transient port-skip messages, SSH proxy errors, and deduplicated repeated guest agent warnings

Refactoring

The codebase was substantially reorganized in this release. A partial inventory:

  • Salon page decomposition — reduced from 2,813 to 945 lines (66% reduction), extracting 8 hooks and 2 components; unified duplicate SSE streaming functions into a shared parser
  • Projects route decomposition — reduced from 1,056 to 49 lines, extracting schemas, 8 action modules, and 4 HTTP method handlers
  • Dead code cleanup — 7 unused files removed, unused exports pruned, duplicate utilities consolidated
  • Plugin dynamic loader — deduplicated 66 lines of bootstrap code from two registries into a shared module
  • Build scripts consolidated — platform-specific shell scripts replaced with cross-platform TypeScript throughout
  • qt-* class migration — 25+ component files migrated from hardcoded Tailwind classes to semantic theme-overridable utilities
  • API conformance — system routes wrapped in context handlers, NextResponse.json replaced with response helpers

Bug Fixes (Selected)

The full changelog runs to several thousand words, so here are the ones that would have kept you up at night:

  • Memories invisible after embedding migration — Float32 BLOB values failed Zod validation; fix adds Buffer.transform() to embedding schemas
  • Chat auto-triggers unwanted second AI response — temporary assistant message missing participantId; turn state didn't recognize the AI had spoken
  • Backup restore in new-account mode — spread-order bug in remapBackupData caused entity cross-references to be silently overwritten
  • WSL2 connection failuresnohup ... & inside wsl.exe --exec sh -c caused the shell to exit immediately and WSL2 to terminate the distro
  • Docker footer showing "(local)" instead of "(Docker)" — nested property access (data.data?.isDocker vs. data.isDocker)
  • Sidebar not appearing after setup wizard — session provider needed a full page reload rather than client-side navigation
  • Google Gemini 3 tool-calling errorssupportsToolCalling() now excludes Gemini 3 models; orchestrator auto-retries without tools on unsupported-tool errors

Timezone Support

Timestamps injected into system prompts now respect the user's local timezone instead of defaulting to UTC. A three-layer fallback chain — per-chat configuration, salon-level settings, QUILTTAP_TIMEZONE environment variable, system default — ensures the right time is always the right time. Electron auto-detects the host OS timezone and passes it through to the VM or container. A searchable IANA timezone selector is available in the Timestamp Configuration card.


Subsystem Table

For those keeping score at home:

Name Function
The Foundry The heart of the operation — architecture, plugins, packages, LLMs, API keys
Prospero Your majordomo, conducting projects, agents, tools, and files
Aurora The complex character model — appearance, clothing, aliases, pronouns, identity
The Commonplace Book Memory and retrieval — a simple RAG that just works
The Salon The chat interface, for conversations with one or many characters
Calliope The user experience — interface, themes, and the Art Deco we like so much
Dangermouse Always in the shadows, managing what the starched-collar providers won't
The Lantern Image generation and atmospheric story backgrounds
Pascal The Croupier — games, randomness, state, and the quiet mathematics of chance
Saquel Ytzama The Keeper of Secrets — encryption, key management, and the Pepper Vault

If you hate the look and feel and weird names, use the Old School theme. It calls things by their boring old names.


Upgrading from 2.x

The database migrations handle themselves. Your data, characters, memories, and chat history will be preserved. The settings have moved — from ten Foundry pages to seven tabbed sections — but everything is where you'd expect it to be if you think about it for more than a moment.

Authentication is gone. If you were using OAuth or Google sign-in, you no longer need to. If you were self-hosting with JWT tokens and reverse proxies, the reverse proxy examples in DEPLOYMENT.md still apply, but the tokens do not.

The Docker deployment is dramatically simpler. If you had a docker-compose.yml with Nginx, MinIO, and multiple services, you may now replace it with a single docker run command or, better yet, the startup script.


The Estate has been renovated, expanded, and — in one or two places — rebuilt from the foundations up. The rooms are the same. The staff are the same. But the house now travels with you, portable and private, sealed against the weather and the world outside. Welcome home.

Installation

macOS

  1. Download the .dmg file and open it
  2. Drag Quilltap to your Applications folder
  3. Launch Quilltap from Applications

Windows

  1. Download and run the .exe installer
  2. Follow the installation prompts
  3. Launch Quilltap from the Start Menu or desktop shortcut

Linux

  1. Download the .AppImage file, make it executable (chmod +x), and run it
  2. Or install the .deb package: sudo dpkg -i quilltap_*.deb
  3. Requires Docker Engine — install from https://docs.docker.com/engine/install/

Node.js (any platform)

npm install -g quilltap
quilltap

On first run, the CLI downloads the application files (~150-250 MB)
and caches them locally. Subsequent launches start instantly.

Docker

docker pull csebold/quilltap:${TAG}

Or download the quilltap-linux-<arch>.tar.gz rootfs tarball for use with Lima. See the README for setup instructions.