The CLI shipped from `management/`, which also holds desktop-only pieces (the Tk control panel, the XFCE panel wiring, the shell wrappers). Packaging that directory meant the wheel either dragged in tkinter or shipped a broken import. Split it: `nexusos_cli/` is what the wheel ships and what `nexus`/`ncp`/ `nexusos` dispatch to, `management/` keeps the desktop half. Alongside the move: * hatch_build.py decides the interface/web/dist include at build time. dist/ is gitignored, so a static force-include aborts `pip install -e .` on a fresh clone - before the reader reaches the `npm run build` step. Editable installs now skip a missing dist; wheels and sdists hard-error naming the command to run. * synapse/proc_util.py gives frontend_manager and ncp process inspection and termination without psutil, which became an optional extra when the wheel landed. It routes around Windows having no signals, where os.kill(pid, 15) is an unblockable TerminateProcess rather than a polite request. * nexusos_cli/monitor.py adds `ncp monitor`, an ASCII dashboard with no curses or rich dependency so it works in Termux, plain SSH and Windows Terminal. Collector and renderer are separate so tests feed fixtures, no stack needed. * tests/test_packaging_deps.py fails the gate when synapse or nexusos_cli import a distribution pyproject does not declare, and when an optional dependency is imported at module scope instead of lazily. * bin/check.sh now builds the wheel, twine-checks it, and asserts the compiled UI and seed playbooks are actually inside it. A wheel that builds but ships no dist/ serves a blank page, which only shows up after release. tests/test_nexus_api.py moves to tests/ with the module it covers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
294 lines
13 KiB
Markdown
294 lines
13 KiB
Markdown
<div align="center">
|
|
|
|
<img src="assets/n-small.png" alt="NexusOS" width="96">
|
|
|
|
# NexusOS
|
|
|
|
**A local-first AI assistant platform.** Runs entirely on your machine — a
|
|
Python/FastAPI backend, an Ollama-compatible endpoint for inference, a
|
|
persistent memory service, and a React frontend. Ollama is local by default;
|
|
Termux and container installs can explicitly point at a separately managed
|
|
endpoint.
|
|
|
|
</div>
|
|
|
|
---
|
|
|
|
## What it is
|
|
|
|
NexusOS ("Nexus") is a self-hosted assistant you actually own. All inference
|
|
runs through **Ollama** on `localhost` by default; 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. A remote Ollama-compatible URL is an explicit configuration
|
|
option for lightweight clients; NexusOS never starts or stops that remote
|
|
process.
|
|
|
|
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.
|
|
|
|
### Python package and portable CLI
|
|
|
|
The portable package installs `nexus`, `ncp`, and `nexusos` as equivalent
|
|
commands. From a checkout today:
|
|
|
|
```bash
|
|
cd interface/web && npm ci && npm run build && cd ../.. # compile the UI
|
|
python -m pip install -e ".[standard]"
|
|
nexus init
|
|
nexus doctor
|
|
nexus serve
|
|
```
|
|
|
|
After a package release, the install becomes `python -m pip install
|
|
"nexusos-ai[standard]"`. The wheel includes the compiled web UI and default
|
|
playbooks; it keeps writable state outside `site-packages`. See
|
|
[the CLI reference](docs/CLI.md) for commands, configuration, and dependency
|
|
profiles.
|
|
|
|
Termux uses the base package with a remote Ollama-compatible provider. Its
|
|
bootstrap and the current Android native-wheel gate are documented in
|
|
[the Termux guide](docs/TERMUX.md).
|
|
|
|
### 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.
|
|
|
|
```bash
|
|
# 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 (backend :8000 — it 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
|
|
|
|
```powershell
|
|
# 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):
|
|
|
|
```powershell
|
|
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
|
|
|
|
```bash
|
|
# Linux
|
|
source Promethean/bin/activate
|
|
|
|
uvicorn synapse.main:sio_app --host 127.0.0.1 --port 8000 --reload # backend (serves the UI too)
|
|
|
|
# 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
|
|
```
|
|
|
|
```powershell
|
|
# Windows — needs the RemoteSigned policy from the Quick start step above
|
|
Promethean\Scripts\Activate.ps1
|
|
|
|
uvicorn synapse.main:sio_app --host 127.0.0.1 --port 8000 --reload # backend (serves the UI too)
|
|
```
|
|
|
|
### 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:
|
|
|
|
```bash
|
|
# Linux
|
|
source Promethean/bin/activate
|
|
pip install <package>
|
|
```
|
|
|
|
```powershell
|
|
# 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:
|
|
|
|
```bash
|
|
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** | `synapse/memory/` | In-process, no second service or model: `curator.py` reads a conversation once it goes idle, `extractor.py` asks the chat model what is worth keeping, `store.py` merges it into the SQLite DB. |
|
|
| **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 curator + playbook/ollama managers
|
|
- `modules/` — auto-discovered feature plugins
|
|
- `interface/web/` — React + Vite frontend
|
|
- `nexusos_cli/` — the portable CLI the wheel ships (`nexus`/`ncp`/`nexusos`)
|
|
- `management/` — nexus-cli.sh wrapper, control panel, desktop theme
|
|
- `bin/` — install, backup/restore, panel + provisioning scripts
|
|
- `assets/` — branding: icons, boot splash, XFCE/GTK theme
|
|
- `data/playbooks/` — active playbook YAML
|
|
- `Promethean/` — Python venv (gitignored, built by the installer)
|
|
|
|
## Configuration
|
|
|
|
- **Ollama host** — `OLLAMA_HOST` env (default `http://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](https://git.enderofwings.com/enderofwings/NexusOS/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](https://git.enderofwings.com/enderofwings/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.
|
|
|
|
---
|
|
|
|
<div align="center"><sub>NexusOS · local AI, self-hosted on <a href="https://git.enderofwings.com/enderofwings/NexusOS">GitNexus</a></sub></div>
|