Exocortex

unlisted

by A. Kitelev

Configurable UI system for Obsidian with ontology-driven layouts

★ 10 starsUpdated 2d agoMIT
View on GitHub

Exocortex

A semantic knowledge management system built on RDF, SPARQL, and ontology-driven architecture. Runs as an Obsidian plugin, CLI tool, or TypeScript library.

License: MIT CI Tests Coverage


Why Exocortex

Most tools separate data from configuration: you write content in one place and configure UI, workflows, and schemas in another. Exocortex takes a different approach — Everything as Knowledge.

Your entities, their properties, UI layouts, workflows, and commands are all described as semantic data in the same Markdown files, in the same vault. Create a new entity type — and the UI adapts automatically. Define a workflow transition — and buttons appear. No code changes, no server, no vendor lock-in.

This is an evolution of the "as Code" paradigm (Infrastructure as Code, Docs as Code):

As CodeAs Knowledge (Exocortex)
Workflow as Code — CI/CD pipelinesWorkflow as Knowledge — status transitions as RDF data, UI buttons generated automatically
Layout as Code — JSON/CSS configLayout as Knowledge — columns, filters, sorting described semantically
Schema as Code — JSON Schema, PrismaSchema as Knowledge — OWL classes and properties as files in your vault

"As Code" describes desired system state. "As Knowledge" describes what things mean and how they relate.

Compared to existing tools:

ExocortexNotionJiraSemantic MediaWikiProtégé
RDF/Semantic✅——✅✅
File-based (git-friendly)✅———✅
UI from ontology✅✅~~✅
Offline-first✅~——✅
For knowledge workers✅✅✅~—
Action layer (commands)✅~✅——

What It Does

  • Semantic knowledge graph — every piece of knowledge is an Asset with UUID, class, properties, and relationships stored as RDF triples
  • SPARQL queries — ask complex questions across your entire knowledge base
  • Modular ontologies (AssetSpaces) — the engine itself is domain-agnostic; every domain vocabulary ships as a separate, versioned, independently-shareable package you mount on demand. See Exo-as-SDK topology.
  • Everything as Knowledge — commands, workflows, property schemas, layouts, and even plugin settings (exo__Setting assets — see docs/explanation/settings-homoiconization.md) defined as vault assets, not hardcoded
  • Installable ontology packages — extend the graph by mounting an AssetSpace from any public or private GitHub repo (assetspace-add in the CLI, Add a knowledge pack in the plugin) — no engine change required
  • Profile (production-ready) — vault-declared homoiconic profiles that drive on-disk AssetSpace materialization via a single Apply profile operation (mount-state strict replace). One vault, multiple contexts, selective sync. See docs/explanation/profile.md.
  • ExoSync — GitHub-backed vault sync via the Exocortex: Sync command: pull → merge → push over the GitHub REST API, with structured 3-way merge and quarantine for unresolvable conflicts (a SHACL merge-gate ships in core but is not yet wired into the plugin). Works on mobile (no git binary required). See docs/how-to/exosync.md.
  • UI/CLI Parity — every capability is reachable from the Obsidian plugin and the CLI; neither client holds exclusive features. The complement of homoiconicity: it keeps the invocation layer open just as homoiconicity keeps the data layer open. See VISION.md.
  • Desktop / Mobile parity — every plugin command works on both desktop and mobile; none is gated desktop-only (a desktop-only dependency such as the git binary is bridged through a cross-platform path — vault.adapter / GitHub REST). The third member of the no-lock-in triad. See VISION.md.
  • Local-first — all data stays on your device, no cloud required

Quick Start

Option 1: Obsidian Plugin (via BRAT)

Best for: Visual knowledge management, daily planning, interactive exploration.

Prerequisites:

  • Obsidian 1.5+ (minAppVersion is 1.5.0)

Install Exocortex via BRAT:

  1. Install BRAT plugin from Obsidian Community Plugins
  2. Open the command palette (Cmd/Ctrl + P) and run "BRAT: Plugins: Add a beta plugin for testing" — in current BRAT (2.0.8+) adding a plugin is a command-palette action, not a settings-tab button.
  3. Enter repository kitelev/exocortex, then select a release from the "Select a version…" dropdown (required — the dialog ignores Add Plugin until a version is picked).
  4. Click Add Plugin and confirm Exocortex is enabled in Community plugins.

BRAT will automatically keep the plugin updated with new releases. For the full walkthrough (Restricted Mode, Windows obsidian:// fallback, version-dropdown gotcha) see the Getting Started Guide.

Next: Follow the Getting Started Guide to bootstrap your vault and create your first Area, Project, and Task.

Note: Layouts appear in Reading Mode (Ctrl/Cmd + E).

Option 2: CLI

Best for: Automation, AI agents, batch operations.

The CLI exposes five core verbs — find, apply, query, index, validate — plus auxiliary commands (classes, create, archive, workflow, and more).

# Run via npx (or npm install -g @kitelev/exocortex-cli)

# Query your knowledge graph — core `exo:` vocabulary, works on any vault
npx @kitelev/exocortex-cli query "
PREFIX exo: <https://exocortex.my/ontology/exo#>
SELECT ?asset ?label WHERE {
  ?asset exo:Asset_label ?label
} LIMIT 20" --vault ~/vault

# Apply a vault-defined command (exocmd__Command) to an asset —
# e.g. a status-transition command that completes a task.
# <cmd> is the command's UUID or its exocmd__Command_cliName slug.
npx @kitelev/exocortex-cli apply <cmd> "tasks/my-task.md" --vault ~/vault --dry-run

Option 3: Core Library

Best for: Building custom applications.

import {
  InMemoryTripleStore,
  Triple,
  IRI,
  Literal,
} from "@kitelev/exocortex-core";

const store = new InMemoryTripleStore();
await store.add(
  new Triple(
    new IRI("obsidian://vault/tasks/my-task.md"),
    new IRI("https://exocortex.my/ontology/exo#Asset_label"),
    new Literal("My first task"),
  ),
);

// match(subject?, predicate?, object?) — undefined acts as a wildcard
const labels = await store.match(
  undefined,
  new IRI("https://exocortex.my/ontology/exo#Asset_label"),
  undefined,
);
console.log(labels.map((t) => t.object.toString()));

See the Core API Reference for the full TypeScript API, including the SPARQL engine and vault-to-RDF conversion services.


Key Features

Asset — The Unit of Knowledge

Every piece of knowledge is a Markdown file with YAML frontmatter:

---
exo__Asset_uid: 965fd5c2-808e-4c7e-8242-e2e5d85bd996
exo__Instance_class:
  - "[[3f9a1c20-0000-4000-8000-000000000001|Concept]]" # any class your ontology defines
exo__Asset_label: "Exocortex"
exo__Asset_relates:
  - "[[7b2e4d80-0000-4000-8000-000000000002|PKM]]"
  - "[[a1c8f600-0000-4000-8000-000000000003|Semantic Web]]"
---
Knowledge content in Markdown...

Wikilinks resolve by UID; the readable alias after | is just for humans. Class membership (exo__Instance_class) points at whichever ontology class your mounted AssetSpaces define — the engine has no built-in domain classes of its own.

Assets are connected through typed relationships. Individual assets are information; connected assets become knowledge.

SPARQL Queries

Ask complex questions about your knowledge:

# Find every asset related to one with a given label — pure core `exo:` vocabulary
PREFIX exo: <https://exocortex.my/ontology/exo#>
SELECT ?asset ?label WHERE {
  ?asset exo:Asset_label ?label .
  ?asset exo:Asset_relates ?other .
  ?other exo:Asset_label "Machine Learning" .
}

Workflow Customization

Status lifecycles are not built into the engine — you define them for any class as regular vault assets, and the UI buttons follow. The plugin picks up custom workflow definitions on load and validates them (falling back to the built-in lifecycle if a definition is malformed).

See the Workflow Customization Guide for the mechanism. Concrete domain lifecycles (e.g. the EMS effort flow Draft → Backlog → Analysis → ToDo → Doing → Done) live in their own AssetSpace, not in core — see that AssetSpace's README.

Ontology-Driven Forms

Create assets with forms generated from your RDF ontology — fields appear based on rdfs:domain, types detected from rdfs:range.

Layout Code Blocks

Embed Layout definitions directly in your notes:

```exo-layout
[[8c4d2e10-0000-4000-8000-000000000004|MyLayout]]
```

Features: wikilink syntax, loading state, error handling, auto-refresh, interactive sortable tables with inline editing.

Ontology Plugins

Install community ontology packages (AssetSpaces) to extend your knowledge graph:

  • CLI: npx @kitelev/exocortex-cli assetspace-add --vault ~/vault --url https://github.com/kitelev/exoas-pmbok adds a single AssetSpace from a public GitHub URL; bootstrap sets up a fresh vault with the SDK floor.
  • Plugin: the Add a knowledge pack and Set up the engine palette commands do the same from Obsidian. (A first-run Setup (getting started) wizard chains them with the recommended URLs pre-filled — see the Getting Started Guide.)

Profile — Vault-Declared Context

Production-ready. Profile is the architectural cornerstone that makes Exocortex genuinely multi-context: one vault, many roles, full privacy/perf isolation per role.

A profile is a regular vault asset (exo__Profile) that declares which AssetSpaces (ontology submodule packages) are active. There is a single operation over it:

  • Apply profile (Exocortex: Apply profile) — a 2-phase-commit, mount-state strict replace. Materializes the profile's effective AssetSpaces (restore from cache or re-pull from GitHub), unmounts the rest; the TS-floor (exo) is never unmounted, while exocmd and shared-identities are optional AssetSpaces. Eliminates the iPhone Obsidian Sync reindex storm; gives a physical privacy boundary between work / personal / reading contexts.

exo__AssetSpace_materialized is a runtime-derived property that reflects current on-disk state in SPARQL and the inline ✅/⏸ badge on AssetSpace pages.

See docs/explanation/profile.md for the full architectural pitch, including the UID-canon privacy model.

Command Palette

Statically registered plugin commands (all prefixed Exocortex: in the palette):

CommandDescription
Setup (getting started)Open the guided first-run setup wizard (token → engine → registry → profiles → apply); re-openable any time
Edit propertiesEdit the active asset's frontmatter properties
Reload layoutRe-render the layout on the active asset
Toggle layout visibilityShow or hide layouts in Reading Mode
Toggle archived assets visibilityShow or hide archived assets in layout tables
Apply profileApply a vault-declared profile — mount-state strict replace of the AssetSpace set (available when filesystem materialization is wired: desktop git, or mobile REST)
Undo last profile applyRevert the most recent profile apply, restoring the previous mount state
SyncExoSync: pull → merge → push the materialized AssetSpace set over the GitHub REST API
Check sync statusReport sync divergence for the materialized AssetSpaces without writing (read-only parity check)
Set up the engineBootstrap a vault with the foundational exo AssetSpace floor
Add a knowledge packAdd an AssetSpace from a public GitHub repository
Push current knowledge packPush the AssetSpace containing the active file to its remote
Show active profileReport the last-applied profile
Remove knowledge pack (advanced)Unmount an AssetSpace from the vault
Reset profile cache (advanced)Clear the profile-switch tarball cache

In addition, vault-defined exocmd__Command assets are registered dynamically as palette commands (the homoiconic command layer) — their set depends on the commands declared in your mounted AssetSpaces, with visibility gated by their preconditions. Asset-creation and status-transition commands come from this dynamic layer rather than being hardcoded in the engine; which ones you see depends on which AssetSpaces you mount.


Architecture

Monorepo with packages sharing a Clean Architecture core. Two runtime entry points (Obsidian Plugin and CLI) both depend on the same domain core and shared grounding-service factories; runtime-specific adapters implement common storage interfaces (IVaultAdapter / IFileSystemAdapter) so domain logic stays runtime-agnostic.

flowchart TB
    subgraph Runtimes["Runtime entry points"]
        Plugin["@kitelev/exocortex-obsidian-plugin<br/>(Obsidian UI, renderers, commands)"]
        CLI["@kitelev/exocortex-cli<br/>(Node CLI, automation, AI agents)"]
    end

    subgraph Shared["Shared (runtime-agnostic) packages"]
        Core["@kitelev/exocortex-core (exocortex)<br/>Domain models · RDF/SPARQL · Services"]
        Services["@kitelev/exocortex-services<br/>Grounding-service factories"]
        Interfaces[["IVaultAdapter · IFileSystemAdapter<br/>(ports defined in core)"]]
    end

    subgraph Adapters["Runtime adapters (implement ports)"]
        ObsAdapter["ObsidianVaultAdapter<br/>ObsidianFileSystemAdapter<br/>(app.vault API)"]
        NodeAdapter["NodeFsAdapter<br/>CachingNodeFsAdapter<br/>(fs/promises)"]
    end

    Plugin --> Core
    Plugin --> Services
    CLI --> Core
    CLI --> Services
    Services --> Core
    Services -. depends on .-> Interfaces
    Core --- Interfaces

    Plugin --> ObsAdapter
    CLI --> NodeAdapter
    ObsAdapter -.implements.-> Interfaces
    NodeAdapter -.implements.-> Interfaces

    classDef runtime fill:#e3f2fd,stroke:#1565c0,color:#0d47a1;
    classDef shared fill:#f3e5f5,stroke:#6a1b9a,color:#4a148c;
    classDef adapter fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20;
    classDef iface fill:#fff8e1,stroke:#f9a825,color:#7d5500,stroke-dasharray: 4 3;
    class Plugin,CLI runtime;
    class Core,Services shared;
    class ObsAdapter,NodeAdapter adapter;
    class Interfaces iface;

Packages

PackagenpmPurpose
exocortexPrivateCore business logic, domain models, SPARQL engine, 35+ services
@kitelev/exocortex-obsidian-pluginPrivateInteractive UI: React components, layout renderers, palette commands, modals
@kitelev/exocortex-cli@kitelev/exocortex-cliCLI for automation, archive/unarchive, SPARQL queries, AI agent integration
@kitelev/exocortex-servicesPrivateShared runtime-agnostic grounding-service factories (RFC 94e520da Phase 1)
@kitelev/exocortex-test-utilsPrivateShared test utilities, mock factories, flaky test reporter

Technical Standards

  • Clean Architecture — clear layer separation (presentation, application, domain, infrastructure)
  • SOLID Principles — especially Single Responsibility
  • Domain-Driven Design — knowledge domain as system center
  • Semantic Web — RDF, SPARQL 1.2, OWL, RDF-Star
  • Local-first — your data stays local, cloud is optional

SPARQL 1.2 Support

FeatureDescription
LATERAL JoinsCorrelated subqueries for "top N per group" patterns
PREFIX*Auto-import prefixes from well-known vocabularies
DESCRIBE OptionsDEPTH and SYMMETRIC control for DESCRIBE queries
Directional Language TagsRTL/LTR text direction support (@ar--rtl)
DateTime ArithmeticNative date/time subtraction and duration operations
NORMALIZE/FOLDUnicode normalization and case folding

Documentation

Getting Started

By Interface

CLI:

  • Run npx @kitelev/exocortex-cli --help for the full list of commands and options (find, apply, query, index, validate, and more)

Core Library:


Development

git clone https://github.com/kitelev/exocortex
cd exocortex
git submodule update --init --recursive
npm install
npm run build
npm run test:all

The git submodule update --init --recursive step hydrates packages/exoas-exo and packages/exoas-exocmd — the TBox ontology + exocmd__Command fixture submodules. Tests that read fixture assets from these submodules on disk fail with an explicit error if they are missing (e.g. grounding-type-vault-fixture-parity.test.ts walks packages/exoas-exocmd/; VaultSettingsRegistry.test.ts reads packages/exoas-exo/exo/). Both submodules are public; no auth token required.

This project is developed primarily by AI agents (Claude Code, GitHub Copilot) following documented patterns. Human contributions welcome!

AI Development Resources

DocumentPurpose
CLAUDE.mdAI agent guidelines, worktree rules
AI Development PatternsPatterns distilled from AI-driven development
Architecture GuideClean Architecture patterns

Vision

For the full philosophical vision, long-term roadmap, and 42 unique IT ideas behind the project, see VISION.md.


License

MIT License — see LICENSE

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.