Guides · · 8 min read

How to Build a QVAC App: A Practical Starting Guide

A practical, step-by-step guide to building your first app on the QVAC SDK, and the constraints to plan around.

So you want to build an app where the AI runs on the user's device — private, offline, cross-platform. QVAC is designed for exactly that. This guide walks through how to think about building on it, from first install to shipping. It's an orientation, not a substitute for the official docs (docs.qvac.tether.io) — treat those as the source of truth for exact APIs.

Before you start: is QVAC the right tool?

QVAC fits when you want on-device AI inside a shipping app, especially cross-platform or mobile, and often across several modalities (text + speech + OCR + retrieval). If you just want to run a model on your own machine, a runtime like Ollama is simpler (here's the comparison). Assuming you're building a real app, read on.

Step 1 — Set up your environment

The QVAC SDK is a JS/TS package (@qvac/sdk) that runs on three JS runtimes:

  • Node.js (≥22.17) for servers and CLI tools
  • Bare for lightweight cross-platform use
  • Expo (≥54) for iOS and Android — physical devices, not Expo Go

Pick the runtime that matches your target. For a mobile app, that's Expo. Install the package:

npm install @qvac/sdk

Step 2 — Load a model

Models can come from a local file, an HTTP URL, or QVAC's distributed model registry (which exposes preconfigured model constants so you don't manage files by hand). The basic lifecycle is load → use → unload:

import { loadModel, completion, unloadModel } from "@qvac/sdk";

const modelId = await loadModel({ modelType: "llm" });

On first run, the model downloads (and can be fetched peer-to-peer). Handle this as part of onboarding — see Step 5.

Step 3 — Run your first inference

const response = completion({
  modelId,
  history: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Summarize the following note: ..." },
  ],
});

Stream the response so the first tokens appear immediately — on-device, perceived speed depends heavily on streaming. When you're done with a model, unloadModel(modelId) to free memory (critical on phones).

Step 4 — Add the modalities you need

Real apps rarely stop at text. The same SDK gives you, behind one interface:

  • Embeddings for semantic search over user data
  • RAG for answering questions from the user's own documents
  • Transcription for voice input
  • OCR for reading text from images
  • Translation for on-device language conversion

Because they share one API, you can combine them without stitching separate libraries. For a worked example of the embeddings + retrieval + LLM pipeline, see how we're building on-device RAG in Local Notes (coming soon).

Step 5 — Design for the on-device realities

Three things that are different from cloud development:

Model lifecycle and memory. You can't keep every model resident on a phone. Decide when to load and unload; this is most of the real engineering.

First-run download is your onboarding. With no server, the initial model download is the first-run experience. Lean into an honest "setting up on-device AI" rather than faking an instant start. QVAC supports pause/resume and sharded downloads to make this smoother.

Latency budget shifts. You lose network latency (great) but gain compute-on-device latency. Warm models, incremental work, and streaming keep it feeling instant.

Step 6 — Ship cross-platform

The payoff: the same codebase runs across iOS, Android, macOS, Windows, and Linux. Write once, and your on-device AI behaves consistently everywhere — no separate per-platform AI stacks.

Optional: OpenAI-compatible server

QVAC ships an HTTP server exposing an OpenAI-compatible API. If you have existing OpenAI-style code, you can point it at local inference with minimal changes — handy for prototyping or migrating a backend to on-device.

Next steps

APIs evolve; always verify exact signatures and requirements against the official documentation.