Getting Started with Quilltap
There are, as with most things worth doing, several paths to the same destination. We have arranged them in order of increasing difficulty, rather like a cocktail menu that begins with champagne and ends with absinthe.
A Frank Word About Your Options
We would very much like to tell you that Quilltap installs with a single click and a wisp of lavender-scented magic. We nearly can. The desktop application now ships with its own Node.js runtime, so in its simplest form you really do just download it, open it, and go. The other paths require you to install something first, but once the machinery is assembled, the setup wizard handles the rest. The nouveau riche will understand… you can't buy the house of your dreams, you have to wait until a team of contractors builds it for you.
The choice between them comes down to two questions: what are you willing to install? and how much do you trust the AI running on your machine? That second question deserves a moment of your attention.
On the Matter of Sandboxes
As AI models grow more capable — reading files, writing code, using tools — the question of where that code executes becomes rather important. A virtual machine is a genuine locked room: if an AI-generated script misbehaves, it misbehaves inside a contained environment with no access to your host system beyond what you've explicitly shared. Docker provides a similar boundary, though somewhat thinner. Running directly on your machine provides no boundary at all — the AI's code is your code, with your permissions, in your house, wearing your slippers, drinking your chamomile tea, and potentially leaving the back door open when it goes out to play.
For most people having a pleasant conversation with a fictional character, this distinction is academic — you likely will not let them do much more than send you a photograph or search the web or read your story. For anyone exploring agentic features — tool use, code execution, file manipulation — it is emphatically not. Choose accordingly.
| The Civilized Way Desktop App — Direct | The Fortified Way Desktop App — VM | The Dockworker's Route Docker | The Shortcut Node.js / npx | |
|---|---|---|---|---|
| You must install | Nothing extra — Electron bundles Node.js | macOS: Xcode CLI Tools Windows: WSL2 Linux: Docker Engine | Docker Desktop (Win/Mac) or Docker Engine (Linux) | Node.js 22+ |
| First launch | Fastest — uses Electron's embedded Node.js, runs directly | Slowest — downloads a VM image (~150 MB), boots a Linux guest | Fast — pulls the container image, starts in seconds | Fast — downloads app files, runs directly |
| Subsequent launches | Near-instant | A few seconds for the VM to wake | Near-instant | Near-instant |
| AI sandbox | ✗ No isolation Runs with your permissions | ✓ Full VM isolation AI code runs in a locked room | ⚠ Container isolation Good boundary, not airtight | ✗ No isolation Runs with your permissions |
| Native window | Yes — Electron desktop app | Yes — Electron desktop app | Optional — Electron or browser | Browser only |
| Multiple data dirs | Yes — managed from splash screen | Yes — managed from splash screen | Yes — mount different volumes | Yes — --data-dir flag |
| Best for | Most users — fastest start, zero prerequisites | Safety-conscious users who want a genuine sandbox | Server deployments, Docker veterans, and Linux users | Quick evaluation, developers, and the impatient |
Our recommendation for most people is the desktop application in Direct mode — it's the fastest path from download to conversation, with zero prerequisites. The Electron app ships with its own Node.js runtime, so you don't need to install anything else. Just download, open, and go.
If you use AI tools that read and write files on your behalf and want a genuine sandbox around that behavior, switch to VM mode from the splash screen. The Electron app lets you toggle between Direct, VM, and Docker runtimes at any time — no commitment required.
One More Warning
You may wonder what harm an AI could do on your machine. It's not just the AI, although that is something. If you enable full tool use, it can run scripts on your machine. Maybe install its own software. Maybe plant something that phones home as soon as you put your credit card number into a site in a browser window. Additionally, Quilltap has a plugin system, and anybody can write a plugin. If you install a plugin, how do you know that the plugin isn't running something on your computer that you don't want it to run?
I have often assured people that told me they trusted me… they shouldn't. I am a stranger on the internet, and I have no business running code on your machine, at least not code that you didn't purposefully sign up for by clicking "Install" or dragging that file into your Applications folder.
If you already have Docker and would rather not wait for a VM to boot, the Dockworker's Route is a perfectly civilized alternative — and if you use the Electron app to start it, it even lets you switch between the runtimes from its splash screen, so you needn't commit to any one on day one.
The npx shortcut is exactly what it sounds like: a quick way in for developers who already have Node.js installed. Excellent for kicking the tires. Less excellent for leaving the AI unsupervised with your filesystem. If you start here and decide to stay, we'd gently suggest graduating to one of the sandboxed options when the novelty of speed wears off and the reality of agentic AI settles in.
The Civilized Way (Recommended)
The simplest and most delightful way to run Quilltap is to install the desktop application. In its default Direct mode, it bundles everything you need — including its own Node.js runtime — so there is nothing else to install. For those who want a sandbox, the app can also run its backend inside a lightweight Linux virtual machine or a Docker container, switchable from the splash screen at any time.
Download
Visit the GitHub Releases page and download the latest stable release for your platform:
macOS
Download the .dmg installer. Open it and drag Quilltap
to your Applications folder.
In Direct mode (the default), you need nothing else — just launch and go. To use VM mode, Quilltap uses Lima with Apple's Virtualization.framework, which requires Xcode Command Line Tools — the app will offer to install them if they're missing.
Windows
Download and run the .exe installer. Follow the prompts.
In Direct mode (the default), you need nothing
else — just launch and go. To use VM mode,
Quilltap uses WSL2, which is built into Windows 10 and 11.
If WSL2 isn't already enabled, run wsl --install in PowerShell
as Administrator and restart your computer. The app checks for this on
startup and will tell you plainly if something is amiss.
Linux
Download the .AppImage file, make it executable
(chmod +x), and run it. Or install the .deb
package: sudo dpkg -i quilltap_*.deb
In Direct mode, the app runs using its own embedded Node.js runtime — no prerequisites. The Linux desktop app can also use Docker Engine as its runtime backend, because Linux was already the sandbox and always knew it.
Launch
On first run, Quilltap will:
- Present a splash screen where you choose your data directory and runtime mode
- Start the backend (instant in Direct mode; a short wait for VM or Docker)
- Open your workspace in a native window
That's it. No configuration files, no environment variables, no incantations.
Runtime Modes
The splash screen lets you switch between three runtime modes at any time:
Direct — Uses Electron's embedded Node.js. Zero prerequisites, fastest startup. Runs with your user permissions (no sandbox). This is the default.
VM — macOS: Lima with Apple's Virtualization.framework (requires Xcode Command Line Tools). Windows: WSL2. Full sandbox isolation — AI code runs in a locked room.
Docker — Requires Docker Desktop or Docker Engine. Container-level isolation — good boundary, not airtight.
Tip: The desktop app lets you manage multiple data directories from its splash screen — one for work, one for fiction, one for experiments. Each remembers its own runtime mode and window position.
The Dockworker's Route (Docker)
If you prefer containers — or you're running a Linux server, or you simply enjoy the gentle hum of virtualization — Docker is a fine choice.
What You'll Need
Docker Desktop (Windows, macOS, or Linux) — or Docker Engine on Linux. That's it.
With the Desktop App
The Electron desktop app includes a Docker runtime toggle right on the splash screen. Install Docker Desktop, launch Quilltap, and switch the runtime from “Direct” to “Docker.” Same native window, different engine underneath.
Standalone (Browser)
Skip the Electron wrapper entirely and access Quilltap through your browser:
docker run -d \
--name quilltap \
-p 3000:3000 \
-e QUILLTAP_TIMEZONE=America/New_York \
-v /path/to/your/data:/app/quilltap \
foundry9/quilltap Open http://localhost:3000 and the setup wizard will guide you through first-time configuration.
Timezone tip: Set QUILLTAP_TIMEZONE
to your IANA timezone (e.g., America/New_York, Europe/London,
Asia/Tokyo) so timestamp injection in chats shows your local time
instead of UTC. The desktop app detects this automatically.
Using the Startup Scripts
For the smoothest Docker experience, use the included startup scripts. They auto-detect your platform, set the correct data directory, find Ollama if it's running, and handle port forwarding automatically.
irm https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.ps1 | iex View the full script
<#
.SYNOPSIS
Quilltap Docker startup script for Windows.
.DESCRIPTION
Detects platform, sets sensible defaults, and starts the Quilltap container.
.PARAMETER DataDir
Data directory on host. Default: $env:APPDATA\Quilltap
.PARAMETER Port
Host port. Default: 3000
.PARAMETER Name
Container name. Default: quilltap
.PARAMETER Tag
Image tag. Default: latest
.PARAMETER RedirectPorts
Comma-separated ports to forward to host (e.g., "11434,3030")
.PARAMETER ExtraEnv
Extra environment variables as an array of "KEY=VALUE" strings
.PARAMETER RestartPolicy
Docker restart policy. Default: unless-stopped
.PARAMETER NoAutoDetect
Skip auto-detection of local services (Ollama, etc.)
.PARAMETER DryRun
Print the docker command without running it
.EXAMPLE
.\scripts\start-quilltap.ps1
.EXAMPLE
.\scripts\start-quilltap.ps1 -RedirectPorts "11434,3030"
.EXAMPLE
.\scripts\start-quilltap.ps1 -DataDir "D:\quilltap-data" -Port 8080
#>
param(
[string]$DataDir,
[int]$Port = 3000,
[string]$Name = "quilltap",
[string]$Tag = "latest",
[string]$RedirectPorts,
[string[]]$ExtraEnv = @(),
[string]$RestartPolicy = "unless-stopped",
[switch]$NoAutoDetect,
[switch]$DryRun
)
$Image = "foundry9/quilltap"
# Detect platform and set default data directory
if (-not $DataDir) {
if ($env:QUILLTAP_DATA_DIR) {
$DataDir = $env:QUILLTAP_DATA_DIR
} elseif ($IsLinux) {
$DataDir = Join-Path $HOME ".quilltap"
} elseif ($IsMacOS) {
$DataDir = Join-Path $HOME "Library/Application Support/Quilltap"
} else {
# Windows
$DataDir = Join-Path $env:APPDATA "Quilltap"
}
}
# Override from environment variables
if ($env:QUILLTAP_PORT -and $Port -eq 3000) { $Port = [int]$env:QUILLTAP_PORT }
if ($env:QUILLTAP_CONTAINER_NAME -and $Name -eq "quilltap") { $Name = $env:QUILLTAP_CONTAINER_NAME }
if ($env:QUILLTAP_IMAGE_TAG -and $Tag -eq "latest") { $Tag = $env:QUILLTAP_IMAGE_TAG }
if ($env:HOST_REDIRECT_PORTS -and -not $RedirectPorts) { $RedirectPorts = $env:HOST_REDIRECT_PORTS }
# Auto-detect local services
if (-not $NoAutoDetect) {
$DetectedPorts = @()
# Check for Ollama on port 11434
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect("localhost", 11434)
$tcp.Close()
Write-Host "Detected Ollama on port 11434"
$DetectedPorts += "11434"
} catch {
# Not running
}
# Merge detected ports with any explicitly specified
if ($DetectedPorts.Count -gt 0) {
$DetectedCsv = $DetectedPorts -join ","
if ($RedirectPorts) {
$RedirectPorts = "$RedirectPorts,$DetectedCsv"
} else {
$RedirectPorts = $DetectedCsv
}
# Deduplicate
$RedirectPorts = (($RedirectPorts -split ",") | Sort-Object -Unique) -join ","
}
}
# Create data directory if it doesn't exist
if (-not $DryRun) {
if (-not (Test-Path $DataDir)) {
New-Item -ItemType Directory -Path $DataDir -Force | Out-Null
}
}
# Build docker run arguments
$DockerArgs = @(
"run", "-d",
"--name", $Name,
"--restart", $RestartPolicy,
"-p", "${Port}:3000",
"-v", "${DataDir}:/app/quilltap"
)
# Pass the host-side data directory so the app can display it in the UI
$DockerArgs += @("-e", "QUILLTAP_HOST_DATA_DIR=$DataDir")
# Add host port forwarding if requested
if ($RedirectPorts) {
$DockerArgs += @("-e", "HOST_REDIRECT_PORTS=$RedirectPorts")
# Linux needs explicit host.docker.internal mapping
if ($IsLinux) {
$DockerArgs += @("--add-host=host.docker.internal:host-gateway")
}
}
# Add extra environment variables
foreach ($env_var in $ExtraEnv) {
$DockerArgs += @("-e", $env_var)
}
# Image
$DockerArgs += "${Image}:${Tag}"
# Display configuration
$Platform = if ($IsLinux) { "linux" } elseif ($IsMacOS) { "macos" } else { "windows" }
Write-Host "Platform: $Platform"
Write-Host "Data dir: $DataDir"
Write-Host "Port: $Port"
Write-Host "Container: $Name"
Write-Host "Image: ${Image}:${Tag}"
if ($RedirectPorts) {
Write-Host "Forwarding: $RedirectPorts"
}
Write-Host ""
if ($DryRun) {
Write-Host "Dry run - would execute:"
Write-Host " docker $($DockerArgs -join ' ')"
return
}
# Check if container already exists
$existing = docker ps -a --format '{{.Names}}' 2>$null | Where-Object { $_ -eq $Name }
if ($existing) {
$running = docker ps --format '{{.Names}}' 2>$null | Where-Object { $_ -eq $Name }
if ($running) {
Write-Host "Container '$Name' is already running."
Write-Host "Use 'docker stop $Name; docker rm $Name' to recreate."
} else {
Write-Host "Container '$Name' exists but is stopped. Starting it..."
docker start $Name
}
return
}
Write-Host "Starting Quilltap..."
& docker @DockerArgs
Write-Host ""
Write-Host "Quilltap is running at http://localhost:${Port}"
curl -fsSL https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.sh | bash View the full script
#!/usr/bin/env bash
set -euo pipefail
# Quilltap Docker startup script
# Detects platform, sets sensible defaults, and starts the container.
#
# Usage:
# ./scripts/start-quilltap.sh [options]
#
# Options:
# -d, --data-dir DIR Data directory on host (default: platform-specific)
# -p, --port PORT Host port (default: 3000)
# -n, --name NAME Container name (default: quilltap)
# -t, --tag TAG Image tag (default: latest)
# -e, --env KEY=VALUE Extra environment variable (repeatable)
# --restart POLICY Restart policy (default: unless-stopped)
# --dry-run Print the docker command without running it
# -h, --help Show this help message
#
# Environment variables (override defaults):
# QUILLTAP_DATA_DIR Data directory
# QUILLTAP_PORT Host port
# QUILLTAP_CONTAINER_NAME Container name
# QUILLTAP_IMAGE_TAG Image tag
IMAGE="foundry9/quilltap"
# Detect platform and set default data directory
detect_defaults() {
case "$(uname -s)" in
Darwin)
PLATFORM="macos"
DEFAULT_DATA_DIR="$HOME/Library/Application Support/Quilltap"
;;
Linux)
PLATFORM="linux"
DEFAULT_DATA_DIR="$HOME/.quilltap"
;;
MINGW*|MSYS*|CYGWIN*)
PLATFORM="windows"
DEFAULT_DATA_DIR="${APPDATA:-$HOME/AppData/Roaming}/Quilltap"
;;
*)
PLATFORM="linux"
DEFAULT_DATA_DIR="$HOME/.quilltap"
;;
esac
}
detect_defaults
# Defaults (env vars override platform defaults)
DATA_DIR="${QUILLTAP_DATA_DIR:-$DEFAULT_DATA_DIR}"
PORT="${QUILLTAP_PORT:-3000}"
CONTAINER_NAME="${QUILLTAP_CONTAINER_NAME:-quilltap}"
IMAGE_TAG="${QUILLTAP_IMAGE_TAG:-latest}"
RESTART_POLICY="unless-stopped"
DRY_RUN=false
EXTRA_ENVS=()
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--data-dir)
DATA_DIR="$2"; shift 2 ;;
-p|--port)
PORT="$2"; shift 2 ;;
-n|--name)
CONTAINER_NAME="$2"; shift 2 ;;
-t|--tag)
IMAGE_TAG="$2"; shift 2 ;;
-e|--env)
EXTRA_ENVS+=("$2"); shift 2 ;;
--restart)
RESTART_POLICY="$2"; shift 2 ;;
--dry-run)
DRY_RUN=true; shift ;;
-h|--help)
sed -n '2,/^$/{ s/^# \?//; p }' "$0"
exit 0 ;;
*)
echo "Unknown option: $1" >&2
echo "Run with --help for usage." >&2
exit 1 ;;
esac
done
# Create data directory if it doesn't exist
if [ "$DRY_RUN" = false ]; then
mkdir -p "$DATA_DIR"
fi
# Build docker run command
CMD=(docker run -d
--name "$CONTAINER_NAME"
--restart "$RESTART_POLICY"
-p "${PORT}:3000"
-v "$DATA_DIR:/app/quilltap"
)
# Pass the host-side data directory so the app can display it in the UI
CMD+=(-e "QUILLTAP_HOST_DATA_DIR=$DATA_DIR")
# Linux needs explicit host.docker.internal mapping for localhost URL rewriting
if [ "$PLATFORM" = "linux" ]; then
CMD+=(--add-host=host.docker.internal:host-gateway)
fi
# Add extra environment variables
if [ ${#EXTRA_ENVS[@]} -gt 0 ]; then
for env in "${EXTRA_ENVS[@]}"; do
CMD+=(-e "$env")
done
fi
# Image
CMD+=("${IMAGE}:${IMAGE_TAG}")
# Run or print
echo "Platform: $PLATFORM"
echo "Data dir: $DATA_DIR"
echo "Port: $PORT"
echo "Container: $CONTAINER_NAME"
echo "Image: ${IMAGE}:${IMAGE_TAG}"
echo ""
if [ "$DRY_RUN" = true ]; then
echo "Dry run — would execute:"
echo " ${CMD[*]}"
else
# Check if container already exists
if docker ps -a --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
echo "Container '$CONTAINER_NAME' already exists."
if docker ps --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
echo "It's already running. Use 'docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME' to recreate."
else
echo "Starting existing container..."
docker start "$CONTAINER_NAME"
fi
exit 0
fi
echo "Starting Quilltap..."
"${CMD[@]}"
echo ""
echo "Quilltap is running at http://localhost:${PORT}"
fi
curl -fsSL https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.sh | bash View the full script
#!/usr/bin/env bash
set -euo pipefail
# Quilltap Docker startup script
# Detects platform, sets sensible defaults, and starts the container.
#
# Usage:
# ./scripts/start-quilltap.sh [options]
#
# Options:
# -d, --data-dir DIR Data directory on host (default: platform-specific)
# -p, --port PORT Host port (default: 3000)
# -n, --name NAME Container name (default: quilltap)
# -t, --tag TAG Image tag (default: latest)
# -e, --env KEY=VALUE Extra environment variable (repeatable)
# --restart POLICY Restart policy (default: unless-stopped)
# --dry-run Print the docker command without running it
# -h, --help Show this help message
#
# Environment variables (override defaults):
# QUILLTAP_DATA_DIR Data directory
# QUILLTAP_PORT Host port
# QUILLTAP_CONTAINER_NAME Container name
# QUILLTAP_IMAGE_TAG Image tag
IMAGE="foundry9/quilltap"
# Detect platform and set default data directory
detect_defaults() {
case "$(uname -s)" in
Darwin)
PLATFORM="macos"
DEFAULT_DATA_DIR="$HOME/Library/Application Support/Quilltap"
;;
Linux)
PLATFORM="linux"
DEFAULT_DATA_DIR="$HOME/.quilltap"
;;
MINGW*|MSYS*|CYGWIN*)
PLATFORM="windows"
DEFAULT_DATA_DIR="${APPDATA:-$HOME/AppData/Roaming}/Quilltap"
;;
*)
PLATFORM="linux"
DEFAULT_DATA_DIR="$HOME/.quilltap"
;;
esac
}
detect_defaults
# Defaults (env vars override platform defaults)
DATA_DIR="${QUILLTAP_DATA_DIR:-$DEFAULT_DATA_DIR}"
PORT="${QUILLTAP_PORT:-3000}"
CONTAINER_NAME="${QUILLTAP_CONTAINER_NAME:-quilltap}"
IMAGE_TAG="${QUILLTAP_IMAGE_TAG:-latest}"
RESTART_POLICY="unless-stopped"
DRY_RUN=false
EXTRA_ENVS=()
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--data-dir)
DATA_DIR="$2"; shift 2 ;;
-p|--port)
PORT="$2"; shift 2 ;;
-n|--name)
CONTAINER_NAME="$2"; shift 2 ;;
-t|--tag)
IMAGE_TAG="$2"; shift 2 ;;
-e|--env)
EXTRA_ENVS+=("$2"); shift 2 ;;
--restart)
RESTART_POLICY="$2"; shift 2 ;;
--dry-run)
DRY_RUN=true; shift ;;
-h|--help)
sed -n '2,/^$/{ s/^# \?//; p }' "$0"
exit 0 ;;
*)
echo "Unknown option: $1" >&2
echo "Run with --help for usage." >&2
exit 1 ;;
esac
done
# Create data directory if it doesn't exist
if [ "$DRY_RUN" = false ]; then
mkdir -p "$DATA_DIR"
fi
# Build docker run command
CMD=(docker run -d
--name "$CONTAINER_NAME"
--restart "$RESTART_POLICY"
-p "${PORT}:3000"
-v "$DATA_DIR:/app/quilltap"
)
# Pass the host-side data directory so the app can display it in the UI
CMD+=(-e "QUILLTAP_HOST_DATA_DIR=$DATA_DIR")
# Linux needs explicit host.docker.internal mapping for localhost URL rewriting
if [ "$PLATFORM" = "linux" ]; then
CMD+=(--add-host=host.docker.internal:host-gateway)
fi
# Add extra environment variables
if [ ${#EXTRA_ENVS[@]} -gt 0 ]; then
for env in "${EXTRA_ENVS[@]}"; do
CMD+=(-e "$env")
done
fi
# Image
CMD+=("${IMAGE}:${IMAGE_TAG}")
# Run or print
echo "Platform: $PLATFORM"
echo "Data dir: $DATA_DIR"
echo "Port: $PORT"
echo "Container: $CONTAINER_NAME"
echo "Image: ${IMAGE}:${IMAGE_TAG}"
echo ""
if [ "$DRY_RUN" = true ]; then
echo "Dry run — would execute:"
echo " ${CMD[*]}"
else
# Check if container already exists
if docker ps -a --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
echo "Container '$CONTAINER_NAME' already exists."
if docker ps --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
echo "It's already running. Use 'docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME' to recreate."
else
echo "Starting existing container..."
docker start "$CONTAINER_NAME"
fi
exit 0
fi
echo "Starting Quilltap..."
"${CMD[@]}"
echo ""
echo "Quilltap is running at http://localhost:${PORT}"
fi
The Shortcut (Node.js)
If you have Node.js installed and want to skip installers entirely, one command does the trick. The CLI downloads the application files on first run (~150–250 MB) and caches them locally. Subsequent launches start instantly.
Note: If you don't already have Node.js, you likely don't need this path. The desktop app now bundles its own Node.js runtime, so The Civilized Way above is faster, easier, and requires no prerequisites at all.
npx quilltap Or install it globally:
npm install -g quilltap
quilltap
Supports --port and --data-dir flags, plus
--update to force a fresh download. Requires Node.js 22 or later.
A word of caution: The npx path runs with your user permissions and provides no sandbox. Excellent for kicking the tires. Less excellent for leaving the AI unsupervised with your filesystem. If you start here and decide to stay, consider graduating to one of the sandboxed options.
First-Run Setup
Whichever path you chose, Quilltap walks you through a short setup wizard on first launch. It generates your encryption key, which protects your entire database at rest using SQLCipher. You can optionally add a passphrase for extra security — if you do, you'll enter it each time Quilltap starts.
Tip: If you add a passphrase, don't lose it. The encryption key it protects is the one value you can't regenerate. Safest to keep both of them, someplace secure but accessible, like a password manager.
The wizard then walks you through provider configuration: provider selection, API key validation, model selection, optional embedding and image setup, and a final test-and-confirm step. One flow creates everything you need in a single pass.
Once setup is complete, you're in.
What's in Your Data Folder
Everything Quilltap needs lives in a single directory. The application tells you exactly where at the bottom of every page.
| Platform | Default Location |
|---|---|
| macOS (Electron) | ~/Library/Application Support/Quilltap |
| Windows (Electron) | %APPDATA%\Quilltap |
| Linux | ~/.quilltap |
| Docker | Wherever you mount /app/quilltap |
Inside that directory:
| Path | Contents |
|---|---|
data/quilltap.db | Your encrypted SQLite database (characters, chats, settings) |
files/ | Uploaded images and attachments |
logs/ | Application logs |
plugins/ | Installed plugins |
Your data stays on your machine. Quilltap never phones home.
Updating
Desktop App
Download the latest release from the Releases page and install it over the existing version. Your data directory is untouched — nothing is lost.
Docker
docker stop quilltap
docker rm quilltap
docker pull foundry9/quilltap:latest
Then re-run your docker run command or the startup script.
Your data folder is untouched — nothing is lost.
npx / npm
npx quilltap --update
Or if installed globally: npm update -g quilltap
Quick Reference (Docker)
| Task | Command |
|---|---|
| Stop Quilltap | docker stop quilltap |
| Start it again | docker start quilltap |
| View logs | docker logs quilltap |
| Follow logs live | docker logs -f quilltap |
Up and running? Splendid. Now let's get you properly acquainted.
Next StepsTroubleshooting
Desktop app won't start (macOS)
In Direct mode, the app should start without prerequisites. If you're using VM mode, ensure Xcode Command Line Tools are installed — the app will prompt you if they're missing. Check Console.app for Lima-related errors. If all else fails, try deleting the VM; the app will recreate it on next launch.
Desktop app won't start (Windows)
In Direct mode, the app should start without prerequisites. If you're using
VM mode, ensure WSL2 is installed: run wsl --install in PowerShell
as Administrator. Check if the distro exists with wsl --list --verbose.
"Port 3000 is already in use"
For Docker, change the host port: replace -p 3000:3000 with
-p 8080:3000, then open http://localhost:8080.
The desktop app handles port conflicts automatically.
Container exits immediately
Check the logs with docker logs quilltap.
Permission errors on the data folder (Linux)
On Docker Desktop (Windows/macOS), permissions are handled automatically. On native Linux, the container runs as a non-root user that may not match your host UID. If you see permission errors, you can match the container's user to your own:
docker run ... --user "$(id -u):$(id -g)" foundry9/quilltap:latest Connecting to Ollama or other local services
The desktop app and startup scripts auto-detect Ollama on its default port (11434)
and handle port forwarding automatically. For Docker, if you need to reach host
services on nonstandard ports, pass them with -r:
irm https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.ps1 | iex -RedirectPorts "11435" curl -fsSL https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.sh | bash -s -- -r 11435 curl -fsSL https://raw.githubusercontent.com/foundry-9/quilltap/refs/heads/main/scripts/start-quilltap.sh | bash -s -- -r 11435
Multiple ports are comma-separated: -r 3030,8080. The container
bridges forwarded ports internally via socat, so localhost works
from inside Docker — no host.docker.internal gymnastics required.