Ported from downstream development. Four independent defects.
1. The memory dump was unrestorable. iterdump() serializes sqlite_vec virtual
tables as a raw INSERT INTO sqlite_master(...) followed by inserts into a
table the replaying connection cannot see, so replaying memory.db.sql died
on "no such table: vec_messages" and left ZERO tables behind. dump_db() now
loads the vec0 extension and filters the derived vec tables out of the
iterdump stream, matched on each statement's target table rather than as a
substring - a chat message whose text mentions vec_messages is an
INSERT INTO "messages" and has to survive.
compare() reported an unreadable dump as "diverged", which read like a real
verdict and made both guards refuse backup AND restore, locking the machine
out of syncing in either direction. Unreadable is now its own verdict.
_extra() compared updated_at against a "" default, but the column is REAL,
so the comparison raises TypeError on the first conversation the other side
lacks - exactly the case it counts. It tests membership first now. The
direction test declared updated_at TEXT, which is why this survived: the
test compared str to str while the field compared str to float.
2. The memory curator invented facts. It attributed the ASSISTANT's words to
the user, wrote absence claims read off the existing-memory block, and added
judgements ("favorite") the user never used. The prompt now scopes the USER
line as the only source, and two deterministic guards drop absence claims
and facts whose distinctive tokens appear nowhere in the user's message -
prompt wording alone did not hold on a 7B curator.
3. _best_vulkan_device scored Mesa's llvmpipe above an integrated GPU, pinning
Ollama to a software rasterizer advertising 31 GiB of "VRAM" - CPU inference
with Vulkan overhead on top. Software rasterizers are dropped.
4. Models.jsx compared catalog names to installed names literally, but Ollama
resolves a bare name to ":latest", so an untagged entry (nomic-embed-text)
read as missing forever and the Required gate never opened. Chatbot.jsx
fetched the model list once on mount although App keeps the page mounted
behind display:none, so a newly pulled model never appeared in the picker
until a full browser reload.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NexusOS
A local-first AI assistant platform. Runs entirely on your machine — a Python/FastAPI backend, a bundled Ollama instance for inference, a persistent memory service, and a React frontend. No external AI provider is called.
What it is
NexusOS ("Nexus") is a self-hosted assistant you actually own. All inference
runs through a locally bundled Ollama on localhost; conversations, facts,
and settings live in local SQLite. It ships with desktop branding (XFCE theme,
icons, boot splash) so it can be run as a full assistant environment on Linux,
not just a web app.
Chat with vision and voice, persistent memory, tool-using playbooks, document RAG scoped to Projects, gated action tools, and full model management — see Flagship features below for what each of those actually does. Rounding it out: full conversation history (persist, search, edit, export) and a browser tail of the service logs.
Flagship features
Chat, vision & voice
- Streaming replies over SSE, with stop/regenerate/edit-and-resend so a bad reply doesn't mean retyping the whole message
- Vision — attach images and route them to a multimodal Ollama model
- Voice — dictate with local Whisper STT instead of typing, and have replies read back aloud
- Extended thinking — a per-message toggle lets a reasoning model show its work without a round-trip to Settings
Persistent memory
A dedicated microservice reads every exchange and decides, via its own Ollama prompt, whether it contains a durable fact worth keeping — a preference, a name, a standing instruction. Saved facts get layered back into the system prompt on every future chat, so Nexus remembers you across conversations without you re-explaining yourself.
Playbooks
Ordered YAML system-prompt records instead of one static prompt. The first playbook is the active persona; the rest are automatically routed in as reference context when relevant. Each playbook can pin its own chat model and its own tool allowlist, so "coding assistant" and "creative writing partner" can be genuinely different setups, not just different wording.
Documents & RAG
Upload PDF, DOCX, TXT, or Markdown; NexusOS chunks and embeds it into a sqlite-vec index and cites the matching passages back into chat answers. Projects scope this per workspace — switch projects and the model only draws on that project's documents (or none, in the unscoped "All" view).
Action tools with an approval gate
- Read-only, run automatically — search memory/history/documents, list models, get the time
- Gated, need approval — search the web, fetch a URL, save a fact. Each one prompts in the chat UI before it runs, so nothing reaches out to the network or writes to memory without you seeing the request first
Model management
List, pull, and delete Ollama models from the UI or ncp, with hardware-aware
recommendations (GPU detection via Vulkan) so a small-VRAM box isn't offered a
model it can't run.
Modules
Feature areas beyond the core assistant live as self-contained plugins instead of being wired into the core app:
- Mail — IMAP/SMTP, multiple accounts
- Network — connection status, WireGuard toggle, ping targets
A backend module is a folder under modules/ with a router.py; a frontend
module is a folder under interface/web/src/modules/ with a module.jsx
exporting a manifest and a component. Both sides are auto-discovered at
startup/build — dropping in a new module folder is enough to have it mounted
and show up in the UI, no registry file to edit.
Quick start
NexusOS runs single-process: the backend on :8000 serves the built web UI
itself, so there's no separate frontend server at runtime. Ollama is started
manually from the app (Start AI in the sidebar), not at boot.
Linux
Nexus was built using an Apple T2 computer running Linux Mint XFCE. The desktop branding (theme, icons, boot splash) could hypothetically be adapted to any Linux distro, but I only support the Ubuntu/Linux Mint package base.
# 1. Build everything: venv (auto-selects AMD/NVIDIA/CPU), web UI, memory DB,
# system packages, Ollama binary and the XFCE desktop wiring.
./install.sh
# 2. Launch (memory :8001, backend :8000 — backend also serves the built UI)
# The install symlinks ncp into /usr/local/bin (sudo); open a new shell first.
ncp web
Python deps are layered: requirements-base.txt (GPU-agnostic core) plus one
GPU overlay — requirements-amd.txt (ROCm) or requirements-nvidia.txt (CUDA).
requirements-windows.txt is the standalone CPU-only runtime (no base overlay).
bin/sync.py picks the right one for the host.
./install.sh is also the update path — re-run it any time to pull the latest
and rebuild. --check dry-runs it; --no-desktop skips the XFCE panel/theme
wiring (that stage is auto-skipped off XFCE anyway). It's a thin wrapper over
bin/sync.py restore, the same code the Windows box runs.
Windows
# 0. Allow scripts to run (PowerShell blocks unsigned scripts by default, which
# stops Nexus's CLI from working). Process scope covers only this window;
# LocalMachine makes it permanent so ncp works from every future shell (run
# PowerShell as Administrator for the LocalMachine line).
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force
# 1. Install Git, then clone (public repo, no account/token needed):
winget install Git.Git
# open a NEW PowerShell window, then:
git clone https://git.enderofwings.com/enderofwings/NexusOS.git nexus-core
cd nexus-core
# 2. Native install — winget Python/Node/Ollama, venv, pip, web build, desktop icon
powershell -ExecutionPolicy Bypass -File .\install-windows.ps1
Then double-click the NexusOS desktop icon, or launch from a new shell
(PATH is read at process start, so already-open windows won't have ncp yet):
ncp web
The app opens at :8000; click Start AI to launch Ollama. The installer
uses requirements-windows.txt (CPU-only, pure-Python — no ML stack, since Ollama
does all inference over HTTP).
Individual services
# Linux
source Promethean/bin/activate
uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload # backend (serves the UI too)
uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload # memory
# Frontend dev server (hot-reload) — only needed when editing the UI;
# production serves the built dist/ from the backend at :8000.
cd interface/web && npm run dev
# Windows — needs the RemoteSigned policy from the Quick start step above
Promethean\Scripts\Activate.ps1
uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload # backend (serves the UI too)
uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload # memory
Promethean
The name for the Python virtual environment all backend code runs in — keeps
Nexus's dependencies out of the system Python. To add a package, activate it
and pip install as usual:
# Linux
source Promethean/bin/activate
pip install <package>
# Windows — needs the RemoteSigned policy from step 0 above, since
# Activate.ps1 is a script PowerShell would otherwise refuse to run
Promethean\Scripts\Activate.ps1
pip install <package>
On Linux, the installer also registers a promethean alias in ~/.bashrc
(and an optional "Promethean Terminal" desktop launcher) that drops you
straight into an activated shell. There's no equivalent on Windows yet — use
Activate.ps1 above, or call Promethean\Scripts\python.exe -m pip install <package> directly without activating at all.
CLI (ncp)
ncp stands for Nexus Control Panel. This was the original method of accessing the frontend and backend power switches before the UI was implemented. The CLI itself is the same Python script on both platforms and behaves identically either way — the exception is ncp panel (the tkinter GUI), which was tuned for Linux/XFCE and looks noticeably more dated on Windows.
Start/stop services and drive the same features as the web UI over the REST API:
ncp start # backend + frontend (--backend|--frontend|--memory)
ncp stop
ncp chat "<message>" # stream a reply
ncp memory list | add <text> | rm <id>
ncp playbook list | show <id> # first playbook (*) = active system prompt
ncp history [query] # recent conversations
ncp doctor [--fix] # diagnostics: venv, Node, imports, Ollama, status
ncp help # see complete help tree
Architecture
| Component | Location | Role |
|---|---|---|
| Promethean (venv) | Promethean/ |
The Python venv all backend code runs in — source Promethean/bin/activate (Linux) / Promethean\Scripts\python.exe (Windows). Keeps deps out of the system Python. |
| Synapse (backend) | synapse/ |
FastAPI app: /chat/stream (+ /chat/approve for gated tool calls), /playbooks, /memory, /models, /documents, /projects, /conversations, /stt, /logs, /settings, /ollama, /frontend, /icons. Assembles the system prompt: active playbook → reference playbooks → memory facts → relevant past snippets → matching documents → web search results. |
| Memory service | synapse/memory/ |
Separate FastAPI app (:8001). /memories/extract uses an Ollama prompt to decide what to persist. Shares the SQLite DB with the backend. |
| Documents / RAG | synapse/memory/store.py |
PDF/DOCX/TXT/MD ingest, chunked and embedded, retrieved via a sqlite-vec index; scoped per Project workspace. |
| Action tools | synapse/tools.py, synapse/search.py |
Read-only tools (search memory/history/documents, list models, get time) run automatically; web_search, fetch_url, and remember require per-call approval from the chat UI. |
| Playbooks | synapse/playbooks/ + data/playbooks/ |
Ordered {id}.yaml records managed by PlaybookManager; each can pin a chat model and a tool list. |
| Modules | modules/ + interface/web/src/modules/ |
Self-contained feature plugins (Mail, Network). Backend: any modules/*/router.py is auto-mounted. Frontend: any interface/web/src/modules/*/module.jsx is auto-registered in the UI. No registry file to edit. |
| Ollama | ollama/bin/ollama |
Bundled binary; OllamaManager handles lifecycle + model selection (Vulkan GPU detection). HTTP API at 127.0.0.1:11434. |
| Frontend | interface/web/ |
React 19 + Vite. Built to dist/ and served by the backend at :8000 (single-process). Pages: Chat, Playbooks, Models, Memory, Documents, Logs, Settings — plus any auto-registered modules (currently Mail, Network). |
Storage
Most data lives in synapse/memory/memory.db (SQLite, WAL) — facts,
conversations, messages, settings, and the document/vector index. Playbooks
are the exception (YAML files in data/playbooks/). All paths are defined in
synapse/nexus_config.py.
Layout
synapse/— FastAPI backend + memory service + playbook/ollama managersmodules/— auto-discovered feature pluginsinterface/web/— React + Vite frontendmanagement/— nexus-cli.sh, ncp API client, control panel, desktop themebin/— install, backup/restore, panel + provisioning scriptsassets/— branding: icons, boot splash, XFCE/GTK themedata/playbooks/— active playbook YAMLPromethean/— Python venv (gitignored, built by the installer)
Configuration
- Ollama host —
OLLAMA_HOSTenv (defaulthttp://127.0.0.1:11434) - Filesystem paths —
synapse/nexus_config.py - Frontend API base URL —
interface/web/src/config.js - Python deps —
requirements-base.txt+ amd/nvidia GPU overlay;requirements-windows.txt= standalone CPU runtime
Issues and feature requests
Bugs — file in Issues
on this repo. Include your OS, GPU vendor, and the relevant slice of
ncp doctor output.
Feature requests and planned work — these live in NexusOS-requests, not here, so the tracker on this repo stays scoped to things that are broken. Use the Requests tab at the top of the repo, next to Issues and Pull Requests, or follow the link above.