Unchained Labs

cortex

A brain your whole household can talk to.

Capture a thought in one keystroke, ask about it later and get a cited answer, and see what is on today — all on a model you run yourself. Shared and personal vaults, peer chat, Obsidian-style editing. Home brain, company brain, club brain: one folder each.

  • alpha
  • 138 tests passing
  • LangGraph + LangChain
  • Python 3.11+ · React 18
  • MIT
61 seconds of the real product: the dashboard, agent streaming, vault edits and channel replies on film are the shipped code — only the model is scripted. Rebuild it with bash docs/promo/build.sh.

Install

A wizard, then a URL

pip install cortxai
cortex setup                  # brain dir → endpoint → models → admin account
cortex serve --host 0.0.0.0   # dashboard on :8642

The wizard pings your endpoint's /models, warns plainly if it is unreachable or public, and creates the first admin account. Docker instead: docker compose up after the one-time setup documented in docker-compose.yml; or the curl-able install.sh.

What it does not do. cortex hosts no model — bring Ollama, vLLM, LM Studio, a LiteLLM proxy, OpenRouter, or an Anthropic key. Vector search is exact cosine in-process: built for personal- and team-sized brains, not millions of chunks. Vault edits are conflict-detected (409 + a banner), not git-merged.

Dashboard

Open it for Today, stay for the rest

Today

The default view: what is on, a few open tasks you tick straight from the list, what changed, and what you wrote on this date in earlier years. Computed without the model, so it answers instantly — and works on a brain with no model configured at all.

Capture

Press c anywhere, or run cortex note "…". One line into today's daily note. No folder to choose, no title to invent — search does not care which note a line is in.

Search

Full-text and vector search fused, over everything you are allowed to read, from / anywhere. It keeps working when the model box is off.

Chat

Private agent threads. Tool calls stream as they run; answers cite files by path, and a citation click opens the file in the vault view. Conversation state lives in a LangGraph checkpoint per thread.

Channels

Peer chat over WebSocket for everyone on the brain. Mention @cortex and the agent replies in-channel — scoped to the shared vault only, streamed token by token as it thinks.

Vault

Shared vault for the group, a private vault per user. CodeMirror editing with Obsidian rendering: wikilinks, embeds, callouts, frontmatter, write-through task checkboxes, tags. Ctrl-S saves; concurrent edits get a conflict banner.

Import

Bring an existing Obsidian vault: zip upload, git URL, or server path. .obsidian/, .git/ and non-vault file types are skipped, and the index catches up in the background.

Admin

Accounts with two roles. Passwords are scrypt-hashed, sessions are HttpOnly signed cookies, and every search or tool call is scoped to what that user may read — filtered inside the query, never trimmed after it.

MCP export

claude mcp add brain -- cortex mcp --brain ~/brain gives Claude Code, Cursor, or Hermes the same tool registry the dashboard agent uses.

Stack

LangGraph agent, pluggable models, hybrid retrieval

The agent is LangGraph's ReAct graph over LangChain chat models with an AsyncSqliteSaver checkpointer. Provider profiles map one OpenAI-compatible wire onto Ollama, vLLM, LM Studio, OpenRouter, and a LiteLLM proxy (its routing policy stays in the proxy — cortex carries no LiteLLM SDK), plus direct Anthropic. Chat and embeddings can come from different endpoints, and roles switch in cortex.yaml, not in code.

providers:
  local:   {kind: openai, base_url: "http://localhost:11434/v1",
            chat_model: qwen3, embed_model: nomic-embed-text}
  router:  {kind: openrouter, api_key_env: OPENROUTER_API_KEY,
            chat_model: anthropic/claude-sonnet-5}
roles:
  chat: router
  embed: local

Retrieval fuses FTS5 and vector cosine with reciprocal rank fusion plus recency — no single scorer is trusted on its own, following Cerebras' knowledge-base write-up. Changing the chunk schema or embedding model re-indexes from scratch; a missing embedding endpoint degrades to full-text and says so. Endpoints are classified by network facts — private, loopback, CGNAT and Tailscale are trusted, public ones get a plain warning that notes will leave the network.

Extend

Four plugin surfaces, one rule: broken plugins are isolated

SurfaceContractWhen it 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; exclude wins over include agent time
Skill skills/<name>/SKILL.md (agentskills.io), instructions load lazily via use_skill on demand
Connector connectors/*.py exposing sync(out_dir, settings); distill, don't dump connectors run

The agent can write, narrowly: add a line to today's note, tick a task by exact path and line, save a web page as markdown. There is no general write-any-file tool — on a vault with no version control, that narrowness is the safety property.

Registration is not authorization: a tool that touches something sensitive keeps its own checks inside the callable. Remembered facts are brain-wide by design — a household brain wants "we water the rosemary on Fridays" visible to everyone, and the agent is told never to remember one person's private secrets.

Managed from the dashboard

The admin-only Extend panel lists every plugin, skill, connector and MCP server with what it provides and its load error if it has one. Write a plugin in the browser and it is loaded before it is saved — code that will not import is refused with the loader's own message rather than breaking the next turn — then the agent is rebuilt so the new tool is live without a restart. Toggling one off never edits your source file, and anything declared in cortex.yaml stays read-only there.

Saving a plugin or connector runs that code on the server as the cortex user — the same trust level as configuring a stdio MCP server. That is why the panel is admin-only. cortex ext list|enable|disable|delete does the same from a terminal.

Restraint

What it will not do to keep you coming back

No streaks, no daily-open counter, no "you have not opened cortex in three days". The daily view is bounded: it shows a handful of tasks and then says that is everything for today. It never shows a growing count of what you have not done — that is a debt counter, and the products that shipped one document the same outcome, which is that people stop opening them.

The digest is a fact, not an opinion: it is computed from your files without asking a model, so it cannot invent a reason to ping you, takes no time to produce, and works when the model box is off. Every large "AI decides what you need each morning" product shipped since 2024 has since been retired or reworked. On a tool you host yourself, your absence is not a problem to be solved.

Lineage

What cortex borrows, and from where

The retrieval design (hybrid fusion, distill-don't-dump connectors) follows Cerebras' knowledge base. The extensibility model — built-in tools, then MCP, then skills — follows Hermes Agent. The SQLite-first storage floor exists because a home brain should not need Postgres, Qdrant, and a vector database cluster to answer a question about rosemary. Usage telemetry keeps preflight's field names, and absent token counts stay absent — unmeasured is not zero.