Metadata-Version: 2.5
Name: cortxai
Version: 0.2.1
Summary: A self-hosted brain for your home, your company, or any activity: pluggable local or cloud LLMs, hybrid memory, and an expandable plugin surface.
Project-URL: Homepage, https://unchained-labs.github.io/cortex/
Project-URL: Documentation, https://unchained-labs.github.io/cortex/
Project-URL: Repository, https://github.com/Unchained-Labs/cortex
Project-URL: Changelog, https://github.com/Unchained-Labs/cortex/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/Unchained-Labs/cortex/issues
Author: Unchained Labs
License: MIT License
        
        Copyright (c) 2026 Clanker Labs
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agent,assistant,knowledge-base,langgraph,llm,mcp,obsidian,rag,self-hosted
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Office/Business :: Groupware
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Text Processing :: Indexing
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.115
Requires-Dist: httpx>=0.27
Requires-Dist: langchain-anthropic>=0.3
Requires-Dist: langchain-core<0.4,>=0.3
Requires-Dist: langchain-mcp-adapters<0.2,>=0.1
Requires-Dist: langchain-openai>=0.2
Requires-Dist: langgraph-checkpoint-sqlite>=2.0
Requires-Dist: langgraph>=0.2.60
Requires-Dist: mcp<2,>=1.9
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: pyyaml>=6.0
Requires-Dist: uvicorn>=0.30
Requires-Dist: websockets>=13
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

<div align="center">
  <img src="docs/assets/lockup-horizontal.svg" width="300" alt="Unchained Labs">
</div>

# cortex

A self-hosted brain for a household or a team: a dashboard where people chat
with each other and with an agent that has read their shared notes — on your
own model, on your own machine.

<div align="center">
  <a href="https://unchained-labs.github.io/cortex/#film">
    <img src="docs/assets/cortex-demo.gif" width="720" alt="cortex demo — ask the agent, watch the tool call stream, get a cited answer">
  </a>
  <br><sub>Real product on film — dashboard, agent, and streaming are the shipped code; only the
  model is scripted (<code>docs/promo/</code> rebuilds it). <a href="https://unchained-labs.github.io/cortex/#film">The full one-minute film →</a></sub>
</div>

**Status: alpha.** The API and config surface are settling; expect breaking
changes between minor versions. The index and checkpoint formats are
disposable caches — deleting `.cortex/` loses conversations, never notes.

```sh
pip install cortxai
cortex setup                 # wizard: brain dir, model endpoint, admin account
cortex serve --host 0.0.0.0  # dashboard on :8642
```

Or `bash install.sh` (pipx/uv/venv autodetect), or `docker compose up` after
the one-time `cortex setup /brain` documented in docker-compose.yml.

**What it does not do:** cortex hosts no model — you bring an endpoint:
Ollama, vLLM, LM Studio, a LiteLLM proxy, OpenRouter, or the Anthropic API.
Vector search is exact cosine in-process, right for personal- and team-sized
brains, wrong for millions of chunks. Vault edits are last-writer-wins with
conflict *detection* (a 409 and a banner), not git-grade merging. The
calendar connector expands no recurrence rules yet.

## The dashboard

- **Chat** — private threads with the agent. It searches before it answers,
  streams its tool calls (⚙ `search_brain` … ✓ 33ms), and cites files by
  path; clicking a citation opens it in the vault view.
- **Channels** — peer chat for the people on the brain. Mention `@cortex` and
  the agent answers in-channel, reading only the shared vault — never
  anyone's personal vault.
- **Vault** — shared and personal vaults, edited in the browser with
  Obsidian-flavored rendering: `[[wikilinks]]`, `![[embeds]]`, `> [!note]`
  callouts, frontmatter, task checkboxes that write through, `#tags`.
  Ctrl-S saves; a concurrent edit gets a conflict banner, not a silent
  clobber.
- **Import** — bring an existing Obsidian vault as a zip upload, a git URL,
  or a server path. `.obsidian/`, `.git/` and non-vault file types are
  skipped.
- **Admin** — accounts (`admin` / `member`), index stats.

Accounts are username + password (scrypt), sessions are HttpOnly cookies.
Each user sees the shared vault, their own vault, and connector sources —
search, grep, and the agent are scoped per request, filtered inside the
query rather than trimmed after it.

## The agent stack

LangGraph's ReAct agent over LangChain chat models, with conversation state
in an `AsyncSqliteSaver` checkpoint per thread:

```yaml
providers:
  local:
    kind: openai                    # Ollama, vLLM, LM Studio — one wire
    base_url: "http://localhost:11434/v1"
    chat_model: qwen3
    embed_model: nomic-embed-text
  router:
    kind: openrouter                # cloud aggregator, OpenAI wire
    api_key_env: OPENROUTER_API_KEY
    chat_model: anthropic/claude-sonnet-5
  claude:
    kind: anthropic                 # direct Anthropic Messages API
    api_key_env: ANTHROPIC_API_KEY
    chat_model: claude-sonnet-5
roles:
  chat: router
  embed: local
```

A LiteLLM proxy is `kind: litellm` with its `base_url` — its routing and
fallback policy stays in the proxy, so cortex carries no LiteLLM SDK.
Endpoints are classified by network facts: private, loopback, CGNAT and
Tailscale addresses are trusted; anything public gets a plain warning that
your notes will leave the network.

Retrieval is hybrid: SQLite FTS5 and vector cosine ranked separately, fused
with reciprocal rank fusion, nudged by recency — the design from
[Cerebras' knowledge base](https://www.cerebras.ai/blog/how-we-built-our-knowledge-base).
The index rebuilds from scratch when the chunk schema *or* embedding model
changes, because silently mixing vector spaces is corruption. No embedding
endpoint means full-text search that says so, not fake vector scores.

## Four ways to extend it

| Extension | Contract | Runs |
| :--- | :--- | :--- |
| Tool plugin | `plugins/*.py` exposing `register(registry)`, or a package with a `cortex.tools` entry point | agent time |
| MCP server | `mcp_servers:` block (stdio or streamable HTTP), attached via langchain-mcp-adapters | agent time |
| Skill | `skills/<name>/SKILL.md` (agentskills.io), loaded lazily via `use_skill` | on demand |
| Connector | `connectors/*.py` exposing `sync(out_dir, settings)` — distill, don't dump | `cortex connectors run` |

A broken extension is reported and isolated, never fatal. Registration is
not authorization: a tool that touches something sensitive keeps its own
checks inside the callable.

**Manage them from the dashboard.** The admin-only **Extend** panel lists
every plugin, skill, connector and MCP server with what it provides, its
load error if it has one, and an enable toggle that never edits your source
file. You can write a plugin or connector in the browser: it is loaded
before it is saved, so code that will not import is refused with the
loader's own message instead of silently breaking the next turn, and a
successful save rebuilds the agent so the new tool is live without a
restart. Connectors get a settings box and a "Run now" button; MCP servers
get a form. Servers defined in `cortex.yaml` show up read-only — the file
stays the owner of what it declares.

Saving a plugin or connector runs that code on the server as the cortex
user. That is the same trust level as configuring a stdio MCP server, and it
is why the panel is admin-only. From the terminal, `cortex ext list`,
`cortex ext disable plugin <name>`, and `cortex ext delete` do the same
management without the browser.

Cortex is also an MCP *server* —
`claude mcp add home-brain -- cortex mcp --brain ~/brain` gives Claude Code,
Cursor, or Hermes the same tool registry, at box-owner scope.

## Layout of a brain

```
~/brain/
├── cortex.yaml        # providers, roles, mcp servers, connectors
├── vaults/shared/     # everyone's notes
├── vaults/<user>/     # each user's private vault
├── sources/           # connector output
├── skills/ plugins/ connectors/
└── .cortex/           # index, checkpoints, usage.jsonl — disposable cache
```

Back it up by copying the folder. Home brain, company brain, club brain:
three folders, three `cortex serve` processes.

## Observability

Every model and tool call appends JSONL to `.cortex/usage.jsonl` with
`prompt_tokens`/`completion_tokens` when the endpoint reports them — absent
counts stay absent rather than becoming zeros, which is what
[preflight](https://github.com/Unchained-Labs/preflight) expects for
calibration. Telemetry never makes a call fail.

## Development

```sh
uv venv --python 3.12 && uv pip install -e '.[dev]'
.venv/bin/pytest                    # 102 tests
.venv/bin/ruff check src tests
cd web && npm install && npm run dev   # SPA dev server, proxies to :8642
```

The frontend contract lives in [docs/product-spec.md](docs/product-spec.md);
cutting a release is [RELEASING.md](RELEASING.md).

Docs: [unchained-labs.github.io/cortex](https://unchained-labs.github.io/cortex/) ·
Brand: [Unchained-Labs/branding](https://github.com/Unchained-Labs/branding) ·
License: [MIT](LICENSE)
