Installation lab

Test Aperio on a clean machine.

These tests answer one practical question: can Aperio install, load its native modules, create SQLite, start HTTP, and serve the setup page somewhere that has none of your developer baggage?

This page is mainly for contributors and maintainers. If you only want to use Aperio, follow the main setup guide. If you specifically want to keep Aperio in Docker, jump directly to Run and keep Aperio.
The automated test environments on this page are disposable; do not put real memories in them.
Before you run anything

Prepare once, then use the matching command.

Run these from the repository root. Keep the terminal open: a passing run prints checkmarks; a failed run prints the evidence-log path.

01 · GET THE CODE
Use a real checkout
The runners stage this repository and read the current Git branch. Do not run them from a copied folder without .git.
02 · INSTALL HOST TOOLS
Pick your dependency
Docker needs Docker Desktop. Linux and desktop tests need an Apple-silicon Mac, Parallels Pro or Business, and prlctl.
03 · RUN ONE TEST
Choose the smallest proof
Use Docker for speed, Linux for native install confidence, or a desktop guest for the actual one-click experience.
04 · READ THE LOG
Keep the evidence
Runner logs are saved in vms/out/. They are private, ignored by Git, and are the first place to look after a red run.
One-time local preparationrepository root
# install the host-side JavaScript dependencies
npm install
# optional: see every available VM command
npm run
The VM runners install dependencies again inside the clean guest. Host node_modules is never copied into a VM.
Run the test

Four workflows, one contract.

Choose the workflow that matches the question you have. Docker is the quickest check; Linux tests a fresh command-line installation; desktop guests test the one-click experience; and the shared checklist verifies an installation you already have. Every workflow ends by checking Node/npm, native modules, SQLite migration, an HTTP bootstrap response, /setup.html, and runtime hygiene.

01 · Fastest pathDocker

Use this when you want a quick answer to “does the current checkout build and start?” Docker packages Aperio in a disposable container, so it does not change your normal installation.

Choose the right goal. The two-command smoke test below exits by itself and deletes its temporary container and data. It does not leave Aperio running, download the AI model, or test a real chat. If you want to use Aperio and keep its data, skip to Run and keep Aperio.
# build the image from this checkout
docker build -f docker/Dockerfile -t aperio:test-local .
# run the disposable image check
npm run vmtest:docker -- --image aperio:test-local
Pass: the runner chooses a non-default host port, creates a temporary volume, polls /api/bootstrap/state, checks /setup.html, then removes the container and volume. A pass proves only that the image built, started, and served the setup page—not that local-AI setup or chat completed.

To debug the disposable test with a known port or a longer readiness window:

VMTEST_DOCKER_PORT=31338 npm run vmtest:docker -- --image aperio:test-local
VMTEST_DOCKER_READY_ATTEMPTS=180 npm run vmtest:docker -- --image aperio:test-local
What does 180 mean? The runner polls /api/bootstrap/state once per second while the container starts. The default is 90 attempts, or about 90 seconds; 180 allows about three minutes. Use it only when Docker is slow to start or pull the image. It does not make Aperio faster.

Run and keep Aperio

Use this path when you want to open the setup wizard, download a model, chat, and keep your data between restarts. Start Docker Desktop, open a terminal in the Aperio repository folder, and use the Copy button to copy the complete block:

# build the current checkout; repeat this after changing Aperio's code
docker build -f docker/Dockerfile -t aperio:test-local .
# create durable storage; Docker reuses it if it already exists
docker volume create aperio-local-var
# start Aperio in the background
docker run -d \
--name aperio-local \
--mount type=volume,source=aperio-local-var,target=/app/var \
-e DB_BACKEND=sqlite \
-e SQLITE_PATH=/app/var/aperio.db \
-e APERIO_LITE=on \
-e APERIO_CONFIG_PRECEDENCE=env \
-p 127.0.0.1:31338:31337 \
aperio:test-local

Docker prints a long container ID when it starts successfully. Open http://127.0.0.1:31338/setup in your browser. Docker cannot open the page for you.

The first local-AI setup is the slow one. Gemma E2B downloads about 3.6 GB across its model and multimedia files. Setup now shows downloaded bytes and an active progress bar; an exact percentage is unavailable while llama.cpp discovers the files. You may close and reopen the browser without losing downloaded data. After download, loading the model can take another minute or two. A first response in CPU-only Docker can take several minutes; later responses may still be slow, but they must not fail with a context-size error.

To watch what Aperio is doing, run the command below in another terminal. Press Ctrl+C to stop watching the log; this does not stop Aperio.

docker logs -f aperio-local

Stop Aperio when you are finished for now. Start the same container later; your setup, database, engine, and downloaded model remain in the named volume:

docker stop aperio-local
docker start aperio-local
If Docker says the name aperio-local is already in use: do not run another copy. Use docker start aperio-local. If it says port 31338 is already allocated, either stop the program using that port or change only the left side to -p 127.0.0.1:31339:31337, then open http://127.0.0.1:31339/setup.

Replace or completely remove it

Removing the container does not remove the named volume. This is useful after rebuilding the image: remove the old container, repeat the docker run block above, and Aperio reuses its model and database.

docker stop aperio-local
docker rm aperio-local
Permanent deletion: run the next command only if you intentionally want to erase the setup, memories, SQLite database, bundled llama.cpp engine, and downloaded models. Docker will not be able to recover them.
docker volume rm aperio-local-var

Advanced: choose a published image

The disposable runner never pulls a missing local tag. Published images must be passed as a complete registry reference, preferably a digest. The <digest> part below is a placeholder:

# easiest: test the newest published image
npm run vmtest:docker -- --image ghcr.io/baiganio/aperio:latest
# or pull it and print its exact immutable digest
docker pull ghcr.io/baiganio/aperio:latest
docker image inspect --format '{{index .RepoDigests 0}}' ghcr.io/baiganio/aperio:latest
# then pass the printed ghcr.io/...@sha256:... value
npm run vmtest:docker -- --image ghcr.io/baiganio/aperio@sha256:<digest>

Find published versions and their digests on GitHub Packages — Aperio. Use :latest for the newest image; use @sha256:... when you need to reproduce one exact image.

02 · Native Linux installApple silicon + Parallels

Use this when you need to know whether a new Linux user can install Aperio from a clean computer. Ubuntu tests the one-line installer; Debian tests the developer-style Git and npm setup.

# install the provider once; this lets Vagrant control Parallels
vagrant plugin install vagrant-parallels
# Ubuntu: download, install, check, and remove the test guest
npm run vmtest:linux
# Debian: clone, install dependencies, migrate, and check
npm run vmtest:linux:debian
Pass: the selected guest is destroyed on success, failure, or interruption. Set APERIO_BRANCH=my-branch before the command to test another branch. A passing result means the clean Linux machine completed the intended installation path.
03 · Real desktop installerpre-created Parallels guests

Use these commands before releasing the macOS or Windows one-click installer. They start from a clean desktop machine and run the installer as a normal user would.

# macOS: make a temporary linked clone of aperio-mac-pristine
npm run vmtest:mac
# Windows: restore the clean snapshot and run the installer
npm run vmtest:windows
Pass: the macOS linked clone is deleted and the Windows guest is restored to its clean snapshot. These commands require the one-time guest setup described below.
If Vagrant is interrupted: inspect first with vagrant global-status and VAGRANT_CWD="$PWD/vms" vagrant status. Destroy only the selected profile, for example VAGRANT_CWD="$PWD/vms" vagrant destroy -f ubuntu-lite.
04 · Run only the shared checklistexisting install

Use this when Aperio is already installed in a target folder and you only want to verify that it starts correctly. It does not create a virtual machine or test the installer; it runs the common health checks directly against the folder you provide.

# macOS/Linux: check an existing Aperio folder
bash vms/smoke.sh /path/to/aperio
# Windows PowerShell: use the same checklist on Windows
.\vms\smoke.ps1 C:\path\to\aperio
The target must contain package.json, node_modules, server.js, db/, and public/. It writes temporary database state under that target's .sqlite/; use a disposable copy if the target contains important data.
Know where to look

What lives inside vms/?

Think of the folder as a test bench: a host remote control, a clean-machine procedure, one shared checklist, and private evidence.

PathRoleTouch it when…
run-vagrant.shHost launcher. Selects ubuntu-lite or debian-dev, starts Vagrant, records output, and destroys the guest.You need a different branch or Linux profile.
VagrantfileMachine definition and provisioning: ARM64 box, 4 GB RAM, 2 CPUs, rsync exclusions, and the commands run inside each guest.You are changing what a clean Linux install means.
smoke.sh
smoke.ps1
Platform-neutral acceptance checklist. It does not create a VM; it proves an already-staged install works.You add a critical install/runtime assertion.
docker/run.shBuild-output checker. Creates a temporary container and volume, tests HTTP/UI, captures metadata, and cleans up.You are validating a Docker image.
mac/, win/Desktop host and guest launchers. They stage files read-only, run the real installer, collect logs, and clean up.You are testing the installer on a clean desktop OS.
out/Timestamped host logs. These may contain private paths and runtime details.A run fails. Read, redact, and never commit them.
One-time desktop setup

Prepare clean guests only when you need them.

Docker and Vagrant are self-contained. Desktop workflows need a pristine machine because they test the real installer, not just the application after npm install.

MACOS · aperio-mac-pristine
Create a pristine macOS ARM VM

Create an Apple-silicon macOS guest, finish account setup, install Parallels Tools, confirm /Volumes/psf/ works, and do not install Node or Aperio. Shut it down and name it aperio-mac-pristine. Each run makes and deletes a linked clone named aperio-mac-run.

WINDOWS · aperio-win-test
Create a clean Windows snapshot

Create a Windows 11 ARM VM, finish setup, install Parallels Tools, create an administrator test user, and do not install Node or Aperio. Shut it down and create the snapshot used by the runner:

prlctl snapshot aperio-win-test -n clean
LINUX · VAGRANT
Let Vagrant create the guest

Vagrant downloads the selected ARM64 box the first time. The recipe maps the checkout to /vagrant-repo but excludes host dependencies, runtime data, SQLite state, and old logs. Do not hand-edit the guest to “fix” a test.

Safety boundary: the macOS pristine VM is never started or deleted by the runner. The Windows runner restores the named snapshot. If a run is interrupted, inspect exact VM names before removing anything.
Understand the result

A green run is evidence, not magic.

The runner changes the environment; the smoke contract asks Aperio the same questions everywhere.

CheckWhat it provesTypical output
ToolchainNode 22+, npm, and dependencies are available in the target.✔ toolchain
Native modulesbetter-sqlite3, sqlite-vec, and sharp load for this OS/architecture.✔ native module
SQLiteA fresh install can create its schema.✔ SQLite migrations
HTTP + UIThe server answers bootstrap state and serves /setup.html.✔ HTTP bootstrap · ✔ UI shell
HygieneTemporary runtime state does not leak into the test user's home directory.✔ runtime hygiene
Where to look next

Read the newest file in vms/out/. For Linux/macOS guests, the runner also collects /tmp/aperio-vmtest.log; Windows writes C:\aperio-vmtest.log. Docker includes container logs and inspect output.

When something fails

Use the failure location to choose your next move.

INSTALL / NATIVE MODULE
Wrong architecture or missing build tools

Confirm the guest is ARM64, remove guest node_modules, and let that guest run npm install. Never copy host dependencies into it.

VM / GUEST READY
Parallels Tools or share problem

The desktop runners require Parallels Tools for prlctl exec and shared folders. Confirm the pristine/snapshot VM name and that the host share is visible before changing the scripts.

DOCKER / IMAGE
The image is missing or never answers

Build the exact local tag first. The Docker runner never pulls a missing local tag. Then inspect the log for image metadata, container output, and the bootstrap timeout.

Still stuck? Share the relevant redacted log from vms/out/, the command you ran, your host OS/architecture, and whether the failure happened during provisioning, install, smoke, or cleanup.