Run the desktop app¶
VERA's desktop app is an Electron application with a React interface and a local Python sidecar. Windows users can install the packaged app from GitHub Releases. The remaining sections describe running and packaging it from a repository checkout.
Install the Windows app¶
Download the VERA.Setup.<version>.exe installer from the
latest GitHub Release, run it,
and then open VERA from the Start menu.
The installer registers .vera archives with Windows. After choosing VERA as
the default app for .vera files, double-click an archive in File Explorer or
on the desktop to open it directly in Document Preview. If VERA is already
running, the existing window is restored and reused instead of starting a
second app instance. The archive opens as a standalone document and becomes
the Search/Ask scope; its parent folder is not automatically added to Explorer.
Use File > Open Folder... when you want VERA to remember and watch the
whole folder as a library.
ChatGPT Bridge (developer-mode PoC)¶
File > Settings → ChatGPT Bridge supervises OpenAI Secure MCP Tunnel against
one approved local library. The packaged sidecar launches mcp-bridge with a
fail-closed policy file; ordinary desktop Search/Ask still use the JSON-lines
sidecar. Tunnel runtime API keys are stored with Electron safeStorage. Setup
and demo checklists live in
desktop-bridge-poc-setup-runbook.md and
desktop-bridge-poc-demo-checklist.md.
The setup wizard collects the approved library, a tunnel ID, a securely stored
runtime key, and a detected or selected tunnel-client executable. It checks
that the selected paths exist and validates the tunnel ID before it enables
Save & Connect. The bridge asks tunnel-client to select a loopback health
port and reads its reported URL rather than assuming a fixed port. Sanitized
client diagnostics are appended to the local sidecar log; API keys are never
written there.
Requirements¶
- Git
- Python 3.10 or newer
- uv
- Node.js and npm
- Source-run (
npm run app:dev) works on Linux, macOS, and Windows. The packaged installer currently targets Windows only.
Clone the repository¶
All remaining commands in this guide run from the repository root.
Install the app dependencies¶
Install the Electron and React dependencies:
The development command uses uv to create or update the Python environment
and install the vera-app sidecar and vera-doc engine.
Start the development app¶
This starts the Vite development server and then opens the Electron window.
Keep the terminal running while using the app. Press Ctrl+C in that terminal
to stop both processes.
Open a PDF or Markdown file from the app's Convert view to create a .vera
archive, or use the native File menu to open an existing archive or document
library.
Desktop conversions default to the PyMuPDF ingest pipeline and the
offline hashing embedder. The Convert view exposes dropdowns for
ingest_pipeline (PyMuPDF in 0.3.0) and
embedding model presets such as sentence-transformers:all-MiniLM-L6-v2 and
openai:text-embedding-3-small, plus
a custom provider:model-id field. Chunking and OCR controls are schema-driven:
the sidecar describe_ingest_pipelines action supplies descriptors, and
PipelineConfigForm renders only advertised fields under a collapsed
Advanced pipeline options section (PyMuPDF includes overlap, OCR DPI, and
a Tesseract OCR language dropdown of bundled/downloadable codes plus Custom
for combinations such as eng+spa). These settings are independent of the Chat
model and are persisted in app settings. npm run app:dev installs the app
extra into the workspace environment. The source-run
sidecar matches packaged releases: one Python process with PyMuPDF,
hashing, ONNX MiniLM, and OpenAI embeddings. Save OPENAI_API_KEY under
File > Settings → Embeddings. Plugins are ordinary pip packages in the same environment
(vera.ingest_pipelines / vera.embedders); CLI users can
pip install "vera[docling]>=0.3.0" or pip install -e <clone> after
vera-ingest 0.3.x. An unavailable selection is disabled or fails with the
resolver error. Save an optional token under File > Settings → Hugging Face
(or set HF_TOKEN in the environment / a local .env from .env.example) for
Hub access used by extras. Conversion progress and the current filename appear in
the footer status bar, so progress remains visible when you switch away from
the Convert view. File > Open convert log..., Convert Open log, and
Settings → Diagnostics open the same append-only file
(userData/logs/sidecar.log: %APPDATA%\VERA\logs\sidecar.log when packaged,
%APPDATA%\@vera\app\logs\sidecar.log in app:dev). Timed convert
steps (elapsed_ms) go there so freeze vs .venv times can be
compared; CLI vera convert still prints those lines on stderr only.
Right-click a folder in Explorer and choose Convert…
to open directory conversion for that folder. To rebuild an existing archive with a different ingest
pipeline or embedding model, right-click the .vera file in Explorer and
choose Reconvert…; Convert opens immediately with a preparing status while
the archive is read, then prefills the current pipeline, embedding, and OCR
settings and turns overwrite on. Convert replaces the .vera you clicked, even
if you renamed it or originally converted with a different output name.
If inspect fails and no sibling source is
listed, Reconvert does not export an embedded original and shows
Could not read archive metadata. Place the matching .pdf or .md next to the
archive, or export the original from Document Info once the archive is
readable. Document Info's OCR line is PyMuPDF-shaped (ocr_engine,
ocr_mode, ocr_pages). Markdown archives store ocr: {} and the UI shows
Unknown mode · 0 pages OCR’d; Docling recovery uses engine /
recovered_pages, so the Info line can look incomplete after a successful
convert. Use vera inspect FILE --json (or sidecar inspect) for the full bag.
In Explorer, click a file to select it, Ctrl/Cmd+click to add or remove it, and
Shift+click to select a range. The checkbox next to a file adds or removes that
row from the same list — unchecking it deselects it, and the highlight and the
Chat/Search “selected document” count stay in sync. Selected .vera files become the Search/Ask
scope and selected PDFs or Markdown files become the Convert list. Click the folder name, empty
Explorer space, or press Escape to search the whole library again.
Use the Chat / Search switch above the center workspace to choose between
LLM-backed conversation and direct retrieval. Search supports hybrid, semantic,
and keyword modes from its composer options. Its ranked passage cards open and
highlight the matching source in the document viewer (Mozilla-style PDF chrome
with a page thumbnail rail and rotate counterclockwise) without adding the query
to chat history. CLI and MCP search return the full ranked list; they do not
apply Ask's relative quality filter.
Customize Ask modes¶
Built-in Chat modes are Ask, Research, and Summarize. Each is a
Markdown file: YAML-ish frontmatter sets retrieval defaults, and the body is
the system prompt. User files live in Electron userData/modes (in
npm run app:dev on Windows, %APPDATA%\@vera\app\modes). On first launch
the app copies ask.md and research.md into that folder if they are
missing so you can edit them; summarize.md stays package-only until you
add a file. User files override built-ins with the same id.
Open the folder from File → Answer Modes Folder… or the mode picker's
Open modes folder…. Click Reload modes (or restart) after edits.
Only flat key: value frontmatter is parsed; nested YAML is treated as
prompt body. Clamped fields:
| Field | Default | Allowed |
|---|---|---|
id |
slug of name |
stable identifier |
search_mode |
hybrid |
hybrid, semantic, keyword |
top_k |
8 | 1–20 |
context_chunks |
1 | 0–3 |
include_figures |
false | boolean |
max_searches |
6 | 1–12 |
max_chunks |
20 | 1–60 |
max_figure_images |
4 | 0–20 |
Ask's LLM search tool also accepts quality: strict (keep hits at least
0.85 of the top score), balanced (0.55, default), or permissive (keep
all). Scores are RRF-based, not 0–1 cosine. If the cutoff would drop every
hit, the single best result is kept. Re-searches skip chunks already cited
in the conversation.
Chat Trace shows prompts, tool calls, and streamed answers. When an LLM
HTTP error includes provider_error_detail, Trace also expands Provider
error details on the error banner. Source loading remains independent of library inspection,
conversion, and indexing. Selecting another citation supersedes the earlier
source request. Large manuals copy into a local viewer cache; if a matching
PDF sits next to the .vera file, that sibling is used instead of extracting
the embedded original. A source load that does not settle within five minutes
is cancelled with an error instead of leaving a permanent footer status.
When Figures is enabled, Search initially returns only figure metadata and captions. Selecting a result loads image previews for that result's referenced figures on demand; unselected result images are not read or sent through the sidecar connection.
Large document libraries¶
Collection indexes are persistent: the app checks their freshness when a library is activated but does not rebuild them automatically. Activating a folder only sets the Search and Ask scope; the corpus opens on the first query. A fresh index makes that first search fast. If an index is missing or stale, the first Search or Ask prompts you to build or update it; choose Don't ask again to keep using recursive search without future prompts for that library. Right-click a folder and choose Build index or Update index to start immediately without that dialog; the badge and footer show progress. Use Inspect in the Info view only when you need library metrics or to revalidate every archive; that operation can take substantially longer for large libraries. Inspection runs on a sidecar worker and the footer reports completed and total archives, the current filename, cumulative chunks, and skipped files. Its request-scoped status clears on either success or failure, independently of simultaneous indexing or conversion activity.
After a build or update starts, indexing runs in the background. The folder's index badge spins. The footer shows completed archives, total archives, the current phase and filename, indexed chunks, and skipped-file count. It switches to a finalizing phase while the validated generation is published. You can continue browsing and using Search or Ask while the existing index, or recursive fallback search, remains available. A completed warning badge means some archives were skipped; select it to review the latest indexing report.
On startup, Explorer collapses inactive folders immediately so every library header stays visible and the last active library stays expanded. Folders show their last verified badge state while VERA checks the current filesystem in the background. A neutral spinner is shown when there is no saved status yet, rather than treating the folder as unindexed.
Parent and empty folders can also be activated as libraries. Nested .vera
files are discovered recursively when there is no saved index configuration.
Explorer lists .vera, .pdf, and .md / .markdown files up to 32 directory levels below a
library root (the root itself is depth 0). Deeper files are omitted from the
tree. The listing payload sets truncated: true when that cap is hit;
Explorer does not show a banner for it. Office and HTML
sources are not listed in Explorer — convert them with vera[docling].
A folder with no .vera files remains active and watched; Search and
Ask report that nothing is searchable until archives are present.
Check or build the app¶
Run the TypeScript checks:
Build the renderer and Electron main process:
Create an unpacked desktop build, including the packaged Python sidecar:
On Windows, packaging writes outside the repo (electron-builder rename locks
inside a watched checkout). The unpacked app is
%LOCALAPPDATA%\Vera\desktop-release\win-unpacked\VERA.exe. Override the
folder with VERA_DIST_OUTPUT.
Create the distributable Windows installer:
This rebuilds the app and Python sidecar and writes VERA.Setup.<version>.exe
into %LOCALAPPDATA%\Vera\desktop-release (and clears any leftover
packages/vera-app/release directory).
Sidecar freeze vendors a VERA-exported MiniLM ONNX graph into gitignored
packages/vera-app/build/ (later builds reuse that snapshot). Torch and
Sentence Transformers are excluded from the freeze.
Plugins in the same environment¶
Source-run (npm run app:dev) and packaged builds use one interpreter.
Search, Ask, indexing, and PyMuPDF conversion all run in
the sidecar. Extra converters are pip packages in that environment, not a
second interpreter. The 0.3.0 sidecar does not run Docling conversion.
The packaged Windows installer freezes PyMuPDF, hashing, ONNX Runtime, and
vera-embed-openai into vera-sidecar.exe. MiniLM
(all-MiniLM-L6-v2) ONNX weights ship inside Setup.exe, so Local semantic
(MiniLM) does not download those files on
first use. Docling / Advanced layout (slower) is not part of the 0.3.0
desktop app; install vera[docling] and convert from the CLI. OpenAI
embeddings are bundled; save OPENAI_API_KEY under File > Settings →
Embeddings. Voyage and Ollama are not bundled.
CLI users who want Docling install it into the VERA environment:
CLI Docling may download layout models into DOCLING_ARTIFACTS_PATH. That
pipeline is not listed in the 0.3.0 desktop Convert view.
Ingest plugins register under vera.ingest_pipelines; embedders register
under vera.embedders. Desktop Convert calls preflight_embedder before writing an
archive. MiniLM runs on ONNX Runtime in the Windows sidecar with a
VERA-exported, SHA256-pinned graph. npm run app:dev vendors that graph
into packages/vera-app/build/minilm before Electron starts (exporting
once with --extra ml if the snapshot is missing), so the app does not
load Sentence Transformers for MiniLM. Without that graph MiniLM falls back
to Sentence Transformers, which is how CLI installs run it.
Other Sentence Transformers models still need uv sync --extra ml. A missing
onnxruntime module in a checkout means that extra is not installed —
run uv sync --extra onnx (or --extra app) and restart the app. See
Creating an ingest pipeline plugin and
Creating an embedding provider.
Convert and embed always run in-process in the sidecar; see
Convert in one sidecar.
Common startup problems¶
uvis not recognized — installuv, open a new terminal, and rerun the command from the repository root.npmis not recognized — install Node.js, open a new terminal, and confirmnode --versionandnpm --versionwork.- Electron dependencies are missing — rerun
npm run app:install. - The Electron window does not open — check the
npm run app:devterminal for a Python sidecar, TypeScript, Vite, or port error before restarting it. If the sidecar fails to import numpy, PyMuPDF, or pdfplumber, pointVERA_APP_PYTHONat the workspace.venvinterpreter and restart. - Sidecar errors hide a Python traceback — packaged IPC omits
tracebackunlessVERA_APP_DEBUGis a truthy value. Source-run still prints[vera-sidecar]stderr. Failed to update Windows PE resources/uv-trampolineAccess denied — Windows Defender or corporate EDR is locking uv's temporary launcher while uv installs a package that ships a console script. The sidecar build prefers the project virtualenv (.venv) and only falls back touv run, so install PyInstaller once and rebuild:
Set VERA_SIDECAR_PYTHON or VERA_APP_PYTHON to use a different interpreter, or exclude the
repository and %TEMP% from real-time scanning, then retry.
- An extra parser or embedder is missing from Convert — the 0.3.0 desktop
sidecar ships PyMuPDF only. Docling is a CLI extra (vera[docling]),
not a Convert dropdown. For other plugins, install into
the same environment the sidecar uses (python -m pip install or
python -m pip install -e <clone>), then restart the app. Raw PYTHONPATH
folders are not discovered. If Search warns that semantic groups were
skipped, the embedder used at convert time is not available in this
sidecar. OpenAI embeddings are bundled (OPENAI_API_KEY under File >
Settings → Embeddings); Voyage and Ollama are not.
Provider request errors¶
LLM authentication, credit, rate-limit, and model errors appear in a compact, dismissible banner. The failed prompt is restored in the composer so it can be edited or retried without restarting the app. HTTP 401 and 403 errors usually require checking the saved API key or account permissions; HTTP 402 errors require provider credits or a lower-cost model.
If a provider has no endpoint that supports image input, VERA retries the request with text only and adds a note to the assistant response explaining that the images were omitted.
While an answer is generating, the send button becomes a stop button. Selecting
it cancels only that answer, stops its active provider stream, and saves the
user prompt plus any streamed response received so far without restarting the
local sidecar. Answer prose appears incrementally as provider tokens arrive.
Assistant answers render GitHub-flavored Markdown plus LaTeX math ($…$,
$$…$$, \(…\), and \[…\]) with KaTeX. VERA withholds inline tool-call markup
and clears any provisional prose from a
turn that ultimately invokes a retrieval tool, so only the final grounded
answer remains visible.
For implementation details and the sidecar protocol, see the desktop app architecture.