Search your photos by what's actually in them — completely offline.
Drop a folder of photos into Ash and it describes each one, then lets you search them the way you'd search anything else: "dogs at the beach," "kitchen," "that trip to the coast." It also finds the connections between photos — the same place, the same object, the same kind of scene — so you can explore your library instead of endlessly scrolling it.
Your photos never leave your computer. Nothing is uploaded, there's no account, and no internet is required. Ash stores only a text description and the file's location — your original photos stay exactly where they are.
- Search in plain language. Type what you remember and Ash finds matching photos using their descriptions.
- Filter precisely. Narrow by
location,scene,object,tag,city,country, or date — start typing a field and pick a value to add a removable filter chip. Anything left over runs a search alongside the filters. - Find photos by where they were taken. If a photo has GPS data, Ash extracts the city, region, and country offline and folds it into search. "budva beach" finds photos taken in Budva that match "beach" — no maps API, no network.
- Browse by theme. Photos automatically group into Animals, Places, Objects, and Scenes, plus the people and things that show up again and again.
- Follow the connections. Open any photo to see its full description, its attributes, and other photos that are visually or contextually related.
- Drag and drop to add photos. Watch each one get scanned with live thumbnails, spinners, and a progress indicator.
You'll need Node 18 or newer. Ash runs on macOS, Windows, and Linux, and installs uv automatically if you don't already have it.
One command installs everything and launches the app:
npm run setup # macOS / Linux
npm run setup:win # Windows (PowerShell)On the first run, Ash:
- Explains what it does and doesn't do — your photos stay local, no account, no tracking, no network.
- Downloads the AI models it needs from HuggingFace (about 3.5 GB, one time only). On Intel or low-memory machines, it warns you first that things will be slower.
After that, drag in some photos and start searching. Next time, just run:
npm start🍎 On macOS, setup also adds Ash to
~/Applications, so you can launch it from Spotlight or Raycast (search "Ash") or by double-clicking — no terminal needed. Move this folder? Re-runnpm run install-appto repoint the launcher.
💡 A graphics card helps a lot but isn't required. See Hardware below for what to expect on your machine.
If you're an AI coding agent (or running setup headless/in CI), the one-command setup mostly works — but there are a few things it does that assume a human at a terminal. Watch for these:
-
Never launch the GUI.
npm run setupends by starting the Electron app, which will hang a headless session. Always setASH_NO_LAUNCH=1:ASH_NO_LAUNCH=1 npm run setup # macOS / Linux $env:ASH_NO_LAUNCH=1; npm run setup:win # Windows (PowerShell)
-
Install
uvfirst on macOS / Linux.setup.shonly offers to installuvinteractively; with no TTY (an agent, CI) it declines and aborts with "Can't continue without uv." Install it before running setup:curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH"
(The Windows script installs
uvautomatically, so this step is macOS/Linux only.) -
Network access is required. Setup downloads the
llama.cpp+llama-swapbinaries from GitHub, and the app downloads ~3.5 GB of models from HuggingFace on first run. A sandbox with no outbound network will fail at these steps. -
Prerequisites: Node 18+ must already be present;
uvfetches Python 3.13 itself. Every step is idempotent, so re-running after a failure is safe. -
Adding Python dependencies? Put them in
sidecar/requirements.txt, then regenerate the lock setup actually installs from:uv pip compile sidecar/requirements.txt --universal --python-version 3.13 -o sidecar/requirements.lock. Anything added elsewhere won't be picked up.
This is the whole point of Ash: everything happens on your own computer. Describing photos, generating search, finding connections — all of it runs locally using AI models on your machine. No account, no API keys, no network calls.
Photos are never copied or uploaded. Ash stores only a description and the file path; the photos themselves stay in their original location.
| Your setup | What to expect |
|---|---|
| Apple Silicon (M1–M4) | Runs on the GPU via Metal with zero config. A base 8 GB M1 handles the bundled models fine; 16 GB is comfortable. |
| Intel Macs | CPU-only — describing a photo can take a minute or more. Fine for small libraries, slow for bulk imports. |
| Windows / Linux | CPU by default — fine for small libraries. For speed, drop a CUDA or Vulkan llama.cpp build into vendor/bin. |
| Memory | 8 GB is the practical minimum. Below that, importing may stall. |
See CHANGELOG.md for the full release history. Latest: 0.3.0 — import photos from a self-hosted Immich server.
Ash runs entirely on your machine. The pipeline looks like this:
Electron (UI) ⇄ Python sidecar (FastAPI) ⇄ khora (embedded) → llama-swap → llama.cpp (local GGUF models)
- Electron app (
electron/,renderer/) — the interface: drag-and-drop, the search bar, the Themes view, and the detail view. It also manages the local runtime, downloading the model files and runningllama-swap. - Python sidecar (
sidecar/) — a FastAPI service that calls a local vision model to describe each photo, then stores everything through khora. - llama.cpp + llama-swap —
llama-serverruns the models;llama-swapputs a single OpenAI-compatible endpoint in front of them and swaps models on demand (vision, embeddings, text). - khora, embedded — a
sqlite_lancebackend with avectorcypherengine: SQLite + LanceDB running in-process. No Docker, no external services. Its entity extraction and resolution are what produce the cross-photo connections, without needing something like Neo4j.
Three local models do the work, all downloaded once (~3.5 GB): SmolVLM2 2.2B for vision (plus its mmproj projector), Nomic Embed v1.5 for embeddings, and Qwen2.5 3B for entity extraction. The OpenAI SDK is used only as the HTTP client for the local endpoint — there's no key and no account.
Run the setup steps by hand
npm run binaries # llama.cpp + llama-swap for this OS/arch
cd sidecar && uv venv --python 3.13 .venv \ # python sidecar
&& uv pip install --python .venv/bin/python -r requirements.lock && cd ..
npm install # electron deps
npm start # runThe app finds the runtime binaries in vendor/bin/ (populated by npm run binaries), or in a packaged bundle's Resources/bin/.
