Abstract
I present Nexus Knowledge Graph (repository name nexus-kg), a personal knowledge graph service that captures skills, projects, concepts, lessons, and experiences as typed nodes joined by typed relationships. The system exposes a FastAPI REST surface and a Typer plus Rich command-line client over a uniform repository contract whose two implementations (Neo4j 5 Community Edition and SQLite 3) are runtime-interchangeable. The contract is encoded as a @runtime_checkable Python Protocol, enabling structural type checks at startup without a class hierarchy. A backend factory attempts a Neo4j Bolt connection at boot and silently falls back to a local SQLite file on connection failure, leaving routes, the service layer, and the CLI entirely backend-agnostic. The same breadth-first traversal endpoint resolves to native Cypher variable-length path queries on Neo4j and to an iterative Python simulation over the relationships table on SQLite, returning structurally identical responses. I report on a reference deployment pre-seeded with 752 nodes across 10 typed categories (Skill, Project, Concept, Lesson, Tool, Experience, Person, Organization, Resource, Goal) and 1,122 relationships across 13 typed predicates (KNOWS, USED_IN, DEPENDS_ON, PART_OF, APPLIED_IN, CREATED, and others). The structured-graph property of the resulting substrate informed downstream personal-AI memory architecture decisions, in particular the preference for typed-relationship retrieval over flat vector similarity at the personal-corpus scale.
Keywords: personal knowledge graph, property graph, Neo4j, SQLite, repository pattern, Python Protocol, runtime structural typing, FastAPI, BFS traversal, dual backend.
1. Introduction
1.1 Motivation
Engineering practice produces knowledge in dispersed forms. Skills accumulate across projects; lessons accrue from problems solved; tools enter and leave a personal stack; people and organisations connect projects across time. Recovering this material as a coherent record traditionally relies on prose artifacts (CVs, portfolios, README files) whose structure is implicit. The relations between entries are not addressable: there is no way to ask which skills were applied in which projects, or which lessons depend on which concepts, without a manual pass over the corpus.
A property-graph representation (Robinson et al. 2015) makes the relations first-class. Nodes carry a typed label and a small property bag; edges carry a typed predicate and join nodes with directionality. Queries traverse the structure rather than scan free text. For personal corpora at the scale of a single engineering career (low thousands of nodes), the representation is ergonomic and the queries are cheap.
1.2 Contributions
This paper reports on the design and implementation of Nexus Knowledge Graph, a personal knowledge graph service intended to operate at the scale of a single engineer. I make three contributions.
1.3 Paper Organisation
Section 2 surveys related work in graph database models, knowledge graph systems, and the Repository pattern. Section 3 describes the layered system architecture. Section 4 details the Repository Protocol and the methodology behind its dual-backend property. Section 5 covers implementation. Section 6 reports the state of the reference instance. Section 7 discusses limitations and future work. Section 8 concludes.
2. Related Work
2.1 Property-Graph Models
Robinson, Webber, and Eifrem (2015) provide the foundational treatment of the property-graph model and motivate graph-native storage for connected data. The model expresses domain entities as nodes, joins them with typed edges that carry their own properties, and supports traversal queries that are local to a starting node. Angles and Gutierrez (2008) survey the broader space of graph database models and place the property graph among RDF, hypergraph, and nested-graph alternatives. Nexus Knowledge Graph adopts the property-graph model: every node carries a label, a unique identifier, a name, and a properties dictionary; every relationship carries a typed predicate, a source, a target, and a properties dictionary.
2.2 Knowledge Graphs
Hogan et al. (2021) survey the modern knowledge-graph landscape, distinguishing open knowledge graphs (Wikidata, DBpedia) from enterprise knowledge graphs and the design choices that separate them. Vrandečić and Krötzsch (2014) describe Wikidata's collaborative knowledgebase architecture. These works target shared, multi-author, web-scale graphs with provenance and reconciliation as primary concerns. Nexus Knowledge Graph occupies a different scale and trust regime: a single author, a single corpus, low-thousands of nodes, no reconciliation. The schema choices (10 fixed node types, 13 fixed predicates) reflect this deliberate narrowing.
Matuschak's Evergreen Notes (Matuschak 2020) articulates the principle that personal knowledge bases should be densely linked, concept-oriented, and organised by associative ontology rather than hierarchical taxonomy. Nexus Knowledge Graph operationalises the principle in graph form: typed nodes are the atomic concepts, typed relationships are the associative links, and traversal is the access pattern.
2.3 The Repository Pattern and Structural Typing
Fowler (2003) describes the Repository pattern as a mediator between the domain model and data-mapping layer, presenting a collection-like interface for accessing domain objects. Gamma et al. (1994) describe the Adapter and Strategy patterns under which a single interface can be satisfied by multiple concrete implementations selected at runtime. Nexus Knowledge Graph composes these: a Repository defined as a Python Protocol (PEP 544 structural typing) plays the role of the abstract interface, and two adapters (Neo4jRepository, SqliteRepository) implement it. The @runtime_checkable decorator (Pydantic and standard-library typing documentation) allows isinstance checks to verify protocol conformance at startup, providing a defensive guard against partial implementations without forcing a nominal class hierarchy.
2.4 Graph Databases and Storage
The Neo4j documentation (Neo4j Inc.) describes the Cypher query language and the variable-length path operator (-[*1..N]->) used by the traversal endpoint described in Section 4.4. SQLite serves as a contrasting baseline: a relational store with no native graph operators, where traversal must be expressed as iterated joins or simulated programmatically. The dual-backend choice is deliberately asymmetric, exercising whether the Repository contract is sufficient when one implementation has graph primitives and the other does not.
2.5 Personal AI Memory
Karpathy (2026) describes a compile-don't-store architecture for LLM-maintained personal wikis as a substrate for personal AI assistants. Nexus Knowledge Graph is the predecessor and structural companion in my broader work: where the LLM wiki is prose-shaped and grep-addressable, Nexus Knowledge Graph is graph-shaped and traversal-addressable. The two are not coupled at the implementation level in this work; the connection is at the level of design intent, with the graph informing downstream personal-AI memory architecture decisions (Section 7.2).
3. System Architecture
Nexus Knowledge Graph follows a clean layered architecture in which each layer depends only on the layer immediately below it. The dependency direction is enforced by import structure: routes import from the service layer, the service layer imports the Repository Protocol, and the two repository adapters import the Protocol but not each other.
3.1 Layer 1: Routes (FastAPI)
The HTTP surface comprises three FastAPI routers: nodes (CRUD and listing), relationships (CRUD and listing by type), and search-and-traversal (full-text search, BFS neighbour traversal, JSON export, JSON import, statistics). Request and response schemas are Pydantic v2 models shared across routes, the service layer, and the CLI client. The same Pydantic models guarantee that the wire format is identical regardless of backend.
3.2 Layer 2: Service (GraphService)
The service layer (GraphService) holds business logic that should not be expressed in the SQL or Cypher dialect of any single backend. It accepts validated Pydantic inputs from the routes and dispatches to the Repository. The service is the only object that holds a reference to the Repository instance; routes do not call the Repository directly.
3.3 Layer 3: Repository Protocol
GraphRepository, defined in app/db/base_repo.py, is the contract that both backends must satisfy. The Protocol is decorated with @runtime_checkable, which permits isinstance(repo, GraphRepository) to verify structural conformance at startup. The interface specifies async methods organised into five categories, listed in Table 1.
Table 1. The five method categories on the GraphRepository Protocol.
| Category | Methods (representative) |
|---|---|
| Node CRUD | create, get, list, update, delete |
| Relationship CRUD | create, list by type, delete, get by node |
| Search and traversal | full-text search, BFS neighbours at configurable depth |
| Export and import | full graph as JSON |
| Statistics | node and relationship counts |
3.4 Layer 4: Adapters
Two adapters live below the Protocol: Neo4jRepository (using the official Neo4j Python driver) and SqliteRepository (using the standard library sqlite3 module). Each adapter is responsible for translating Protocol-level method calls into backend-specific queries and for reshaping the results into the shared Pydantic types.
3.5 The Backend Factory
factory.py encapsulates backend selection. At application startup, the factory attempts a Neo4j Bolt connection using the configured URI. On connection failure, the factory instantiates a SqliteRepository pointing at a local database file. The selected adapter is held as the service's Repository reference for the lifetime of the process. No code above the factory observes this decision.
3.6 The CLI Client
The CLI (nexus) is a separate process built with Typer for command parsing and Rich for terminal output. It communicates with the running API server over HTTP via httpx. The CLI does not import the Repository Protocol, the service, or any adapter; it speaks only the wire format. The NEXUS_API_URL environment variable allows the CLI to point at a remote server.
4. Methodology
4.1 The Repository Protocol as Contract
The contract-first design is the load-bearing decision. Defining GraphRepository before either adapter fixes the surface that the rest of the application can rely on. New adapters (in principle: Postgres with the Apache AGE extension, in-memory dictionaries for tests, a remote service over HTTP) can be added by satisfying the same Protocol without touching routes, the service, or the CLI.
4.2 Runtime Structural Typing
Python's typing.Protocol (PEP 544) provides structural subtyping: a class satisfies a Protocol if it has the right method signatures, regardless of inheritance. The @runtime_checkable decorator extends this to runtime, permitting isinstance checks that the standard library refuses on plain Protocols. This admits a defensive startup guard: the factory verifies that the chosen adapter satisfies GraphRepository before returning it. A partially-implemented adapter fails fast with a clear error rather than crashing on a missing method during a later request.
4.3 Connection-Attempt-Then-Fallback
The factory's selection rule is simple by design. Try Neo4j first, since the primary deployment target is the graph-native store. If the Bolt connection fails (the daemon is not running, the credentials are wrong, the network is unreachable), fall back to SQLite. The factory does not negotiate or retry; either Neo4j is reachable now, or it is not. This rule has two operational consequences. A developer can clone the repository and run the API on a laptop with no infrastructure, getting full functionality on the SQLite path. A production deployment can ship with a Neo4j container alongside the API, getting native graph performance on the Neo4j path. Switching between regimes requires only environment configuration.
4.4 Uniform BFS Traversal Across Asymmetric Backends
The neighbour traversal endpoint exposes breadth-first search at a configurable depth from a starting node. The endpoint signature is the same on both backends: /api/graph/{id}/neighbors?depth=N. The implementations diverge sharply.
On Neo4j, the adapter issues a Cypher variable-length path query of the form MATCH (n {id:$id})-[*1..N]-(m) RETURN .... The query optimiser handles the traversal natively, returning all reachable nodes and edges within N hops in a single round trip.
On SQLite, the adapter simulates the traversal in Python. Starting from the seed node, it iteratively queries the relationships table for outgoing and incoming edges, accumulates the visited set, and repeats until depth N is exhausted or no new nodes are discovered. Cycles are broken by the visited set.
Both implementations return the same Pydantic-typed payload. The asymmetry is invisible above the Protocol. This is the strongest empirical evidence that the contract is correctly drawn: a method whose efficient implementation depends on graph primitives can still be expressed under it, because the Protocol fixes the result shape rather than the execution strategy.
4.5 Schema Design
The schema is intentionally narrow. Ten node categories and 13 relationship predicates cover the conceptual space of a single engineering career without reconciliation costs. Adding a category is a deliberate design decision rather than a free operation, which limits drift over the lifetime of the corpus.
Table 2. Node categories in the Nexus Knowledge Graph schema.
| Category | Intent |
|---|---|
| Skill | A capability practiced over time |
| Project | A unit of completed or in-progress work |
| Concept | An idea or pattern referenced across projects |
| Lesson | A specific learning extracted from experience |
| Tool | A library, framework, or instrument used |
| Experience | A role, position, or significant engagement |
| Person | A collaborator or contact |
| Organization | A company, institution, or group |
| Resource | A book, paper, course, or external artifact |
| Goal | A target capability or outcome |
Relationship predicates are similarly fixed. Representative predicates include KNOWS (Person to Person or Person to Skill), USED_IN (Tool or Skill applied within a Project), DEPENDS_ON (Concept or Skill prerequisite), PART_OF (containment), APPLIED_IN (Skill applied in an Experience), and CREATED (authorship). Thirteen predicates total.
5. Implementation
5.1 Stack
The implementation uses Python 3.11+ throughout. FastAPI provides the HTTP server. Pydantic v2 provides validation and serialisation. Neo4j 5 Community Edition provides the primary backend; the official Neo4j Python driver issues Cypher queries over Bolt. SQLite 3 provides the fallback backend through the standard library. Typer plus Rich provides the CLI. httpx is the HTTP client used by the CLI and the seed script. pydantic-settings handles environment-variable configuration. Docker and Docker Compose package the Neo4j container for development and deployment.
5.2 Project Structure
The repository is organised so that each architectural layer is a sibling directory under app/. Routes live under app/api/, the service layer under app/services/, the Protocol and adapters under app/db/. The nexus CLI is registered as a console-script entry point through pyproject.toml, making it installable with pip install -e ..
5.3 Seed Pipeline
The pre-built knowledge base is distributed as JSON files alongside the source code. A dedicated seed script reads the JSON, constructs Pydantic instances for each node and relationship, and submits them to the API via httpx. The seed pipeline therefore exercises the same Protocol surface as ordinary client traffic. There is no privileged path that bypasses the API to write to the backends directly. This property keeps the contract honest: anything the seed pipeline can express is something a future client can express.
5.4 Containerisation
Docker Compose orchestrates the Neo4j container alongside the API for development and deployment. The compose file exposes the Neo4j Bolt port and the Neo4j browser port, mounts a persistent volume for the database files, and configures the API container with the corresponding NEO4J_URI environment variable. A developer who prefers the SQLite path can simply omit the Neo4j service from the compose invocation; the factory falls back automatically.
6. Results
6.1 Reference Instance Scale
Table 3 reports the scale of the pre-seeded reference instance.
Table 3. Reference-instance scale.
| Metric | Value |
|---|---|
| Node categories | 10 |
| Relationship predicates | 13 |
| Total seeded nodes | 752 |
| Total seeded relationships | 1,122 |
| Mean relationships per node | 1.49 |
6.2 Backend Interchangeability
The reference instance was loaded against both backends from the same JSON seed. The two loaded states return structurally identical responses on every Protocol method. Statistics, search, and traversal calls produce the same shapes; counts agree exactly. The only observable difference at the wire level is response latency on traversal queries: Neo4j returns BFS at depth 3 in tens of milliseconds against the seeded graph, while SQLite returns the same payload in hundreds of milliseconds, which remains acceptable for personal-scale queries.
6.3 Protocol Coverage
Both adapters implement all five method categories specified in Table 1: node CRUD, relationship CRUD, search and traversal, export and import, and statistics. The @runtime_checkable startup verification passes for both adapters. There is no method on the Protocol that is implemented on only one backend.
6.4 CLI End-to-End
The nexus CLI exercises the public API surface end-to-end: health checks, typed-node creation (skill, lesson, project, and others through the same path), relationship creation between nodes by id, full-text search by keyword, listing with category filters, and full-graph export and import via JSON. All commands route through httpx to the API server and return Rich-formatted output. Pointing the CLI at a remote server requires only the NEXUS_API_URL environment variable.
7. Discussion
7.1 Asymmetric Adapters and Contract Sufficiency
The dual-backend choice deliberately exercises an asymmetry. Neo4j has variable-length path operators; SQLite does not. A Repository contract that secretly favours one backend would manifest at this exact endpoint, either by failing to express BFS on the relational side or by leaking a Cypher-shaped abstraction into the Protocol. The chosen contract instead fixes the result shape and lets each adapter pick its execution strategy. The Cypher-versus-Python divergence is hidden inside the adapter boundary, where it belongs. This is consistent with the Adapter and Strategy patterns of Gamma et al. (1994).
7.2 Graph Substrate as Personal AI Memory
The structured-graph property of the resulting substrate informed downstream personal-AI memory architecture decisions. Typed relationships outperform pure vector similarity for personal-experience retrieval at the scale this work targets. A query of the form "which skills did I apply on which projects" is a single graph traversal; the same query against a flat embedding store is a multi-step similarity search with ambiguous selection. The empirical observation, validated against the 752-node seed graph, was that typed retrieval is both faster and more deterministic on personal corpora. This observation later shaped the choice of graph-shaped storage for downstream personal-AI memory systems.
7.3 Limitations
7.4 Future Work
8. Conclusion
I have presented Nexus Knowledge Graph, a typed personal knowledge graph service whose central design choice is a runtime-checkable Repository Protocol that admits two interchangeable backends under a single contract. Neo4j 5 supplies graph-native performance when reachable; SQLite 3 supplies a zero-dependency portable fallback when not. Routes, the service layer, and the CLI client are unaware of which backend is active, validated by a uniform BFS traversal endpoint that resolves to native Cypher on Neo4j and to an iterative Python simulation on SQLite while returning structurally identical payloads. A reference instance pre-seeded with 752 nodes across 10 typed categories and 1,122 relationships across 13 typed predicates demonstrates the architecture under realistic personal-scale conditions. I conclude that contract-first design, structural typing at the boundary, and an honest connection-attempt-then-fallback policy together produce a backend-portable knowledge graph service whose substrate is suitable as an input to downstream personal-AI memory systems.
References
---
The contract is the Protocol. The Protocol admits Neo4j when it is reachable and SQLite when it is not. The application above does not need to know.
