BrainAPI is a knowledge graph–powered AI memory layer that transforms unstructured data into structured knowledge, enabling intelligent search, recommendations, and contextual memory for AI agents and applications.
Retrieval systems built on vector similarity return plausible-sounding answers with no way to verify the reasoning. When a user asks a multi-step question like "Who organized AI events in London in Q1 2024?", a nearest-neighbor match on embeddings gives a guess, not a traceable path. Teams need answers grounded in explicit relationships between entities—who did what, to whom, when—rather than statistical proximity.
What This Does
BrainAPI is a knowledge-graph memory layer that converts unstructured text into an event-centric graph. A swarm of agents (src/core/agents/) reads documents, extracts facts, and writes nodes and relationships into a graph store. Queries return answers plus the result.triples path used to derive them.
The repo is a portfolio of five projects: the core Python service (src/), a TypeScript web console (console/), a TUI installer (tui/), a Rust MCP transport bridge (mcp-stdio-http-bridge/), and deployment scripts. The core service supports Neo4j (src/lib/neo4j/client.py) and PostgreSQL/NetworkX (src/lib/postgresql/), with embedding adapters (src/adapters/embeddings.py) and a caching layer (src/adapters/cache.py).
How It Is Wired
Execution enters through the MCP server (src/services/mcp/main.py). _search_memory_sync (line 263) reaches 55 functions; _search_semantically_sync (line 285) reaches 63; _traverse_graph_sync (line 199) reaches 62. These route through guard_brainpat -> get_brains_list, hitting the database via collection.find in two hops. The API lifespan (src/services/api/app.py:40) reaches 29 functions and publishes errors over the network via client.post.
The highest-blast-radius functions are set (called from 41 places), ensure_database (32), Node (28), and get_brain (25). src/config.py is a hub with 36 modules depending on it; src/adapters/graph.py has 25 dependents and 724 lines. Three modules (src/core/agents/architect_agent.py, src/workers/tasks/ingestion.py, src/core/agents/janitor_agent.py) participate in circular imports, which makes refactoring them risky.
Configuration lives in .env.example (copy to .env). The API runs on port 8000, MCP on 8001. A Dockerfile and example-docker-compose.yaml exist for containerized deployment. The Makefile and pyproject.toml imply make and poetry/uv workflows, but the README's TUI path is the documented route.
Real-World Use
Feed the system a sentence: "Emily organized the AI Ethics Meetup in London on March 8, 2024." Later, query: client.retrieveContext("Who organized AI events in London in Q1 2024?"). The response includes the answer and a triples list showing the graph path—Emily → organized → Meetup → located_in → London. That trace is auditable and explainable, which matters for compliance-heavy domains.
High – Deep nesting (25 cases): src/adapters/graph.py reaches indentation depth 7; control flow is hard to follow.
High – Oversized files: src/adapters/graph.py (724 lines), src/core/agents/architect_agent.py—changes ripple widely.
High – Hub modules: 36 files depend on src/config.py; churn here is high-blast-radius.
High – Import cycles: architect_agent.py, ingestion.py, janitor_agent.py are mutually reachable.
Medium – Broad exception handling (16 cases) in src/utils/cleanup.py and others.
High – CI never runs the 14 test files; a green check means nothing.
High – Unpinned GitHub Actions (@v3 tags) and wildcard CORS (allow_origins=["*"]) in src/services/api/app.py.
High – tokio@1 carries CVE-2021-45710; 12 dependencies are multiple majors behind.
The Bottom Line
BrainAPI is a serious attempt at explainable retrieval, with a well-structured core and a real graph-based answer path. The architecture is sound but carries technical debt—deep nesting, hub modules, and circular imports will make changes painful. Use it if you need auditable, relationship-grounded answers and can invest in the CI and dependency hygiene it currently lacks.
What the analyser found
Deployment readiness
7/7
✓Container image
✓CI pipeline
✓Lockfile committed
✓Test suite
✓README
✓License
✓No committed secrets
Composition
358 files
Python240
TypeScript49
TSX12
YAML11
JSON8
Rust6
ReactFlaskDockerKubernetesVector storeNeo4j
Architecture
Top-level areas of the codebase, sized by module count. Arrows show how many imports cross from one area into another.
Ranked by severity × confidence × production reach. Reach is the honest discriminator across a collection that is mostly other people's code: the same finding matters more in something that ships.
high4
medium7
low2
Pin third-party GitHub Actions to a commit SHAhigh7 occurrences
A tag can be moved, so the action running with your token and secrets is whatever its owner last pushed; this is how tj-actions/changed-files leaked secrets from thousands of repos.
Fix: Replace each @vN with the 40-character commit SHA, keep # vN as a comment, and let Dependabot bump the SHAs.
Upgrade the pinned dependency carrying a published advisoryhigh10 occurrences
tokio@1 CVE-2021-45710 (high) +7 more; futures-util@0.3 CVE-2020-35908 (medium) +1 more
The pinned version is the one that installs, so a published advisory against it describes this deployment rather than a hypothetical one, and the advisory is also a public description of how to exploit it.
Fix: Upgrade to the fixed version named in the advisory, then commit the lockfile.
Replace the wildcard CORS origin with an explicit allow listhigh
src/services/api/app.py
allow_origins=["*"]
Any page on the internet can call the API with the browser's cookies attached, so a logged-in visitor to an unrelated site performs authenticated requests without knowing, and the wildcard combined with credentials is the exact configuration browsers refuse for that reason.
Fix: List the origins the API actually serves, and never pair a wildcard with credentials.
Make CI invoke the test suite it hashigh14 occurrences
.github/workflows
14 test files, no test command in any workflow
A green check that never executed an assertion is worse than no check, because reviewers trust it.
Fix: Add a test step to the existing workflow rather than a new workflow.
Declare least-privilege permissions for GITHUB_TOKENmedium
.github/workflows/staging.yaml
1 workflow(s) declare no permissions
With no declaration the token inherits the repository default, so any injected step can push commits or mint releases from inside your own CI.
Fix: Add permissions: contents: read at the top of the workflow and widen per job only where needed.
Enable Dependabot or Renovatemedium
5 manifest(s), no update bot configured
Without a bot a published advisory sits unpatched until someone audits by hand, which across 1,322 repositories means never.
Fix: Commit .github/dependabot.yml covering the repo ecosystems plus github-actions.
Pin the container base image by digestmedium2 occurrences
Dockerfile
python:3.11-slim, python:3.11-slim
An untagged or mutable base means today's build and last month's contain different libc and a different CVE set, with no record of which shipped.
Fix: Use image:tag@sha256:<digest> and enable Dependabot's docker ecosystem.
Gate pull requests on a dependency vulnerability scanmedium
.github/workflows
no dependency scan in CI
This is the one gate that would catch a known-vulnerable package before it reaches a build, and no repository in the sample had it.
Fix: Add dependency-review-action on pull_request, or osv-scanner on push and a schedule.
Set persist-credentials: false on checkoutmedium
.github/workflows/tags.yaml
checkout keeps the token, then dependencies are installed
The token stays in .git/config for every later step, so a malicious postinstall script reads a pushable credential without one ever being passed to it.
Fix: Add with: persist-credentials: false, and pass an explicit token only to the step that pushes.
Add a non-root USER to the imagemedium
Dockerfile
CMD or ENTRYPOINT with no USER directive
A process running as root in the container is root against every mounted volume, and it turns any container escape or writable-mount mistake from a contained problem into a host one.
Fix: Create an unprivileged user, chown what it needs, and end the Dockerfile with USER.
Expand the test suite; it cannot cover this codebasemedium14 occurrences
14 test files against 308 source files (ratio 0.045)
A reviewer reads "has tests" and assumes a safety net that covers two files out of eighty-five, which is how untested paths ship behind a green badge.
Fix: Add tests for the highest fan-in modules first.
Set timeout-minutes on the workflow jobslow3 occurrences
.github/workflows/deploy.yaml
3 workflow(s) declare no job timeout
A wedged step runs to the six-hour platform default, which on a two-hourly schedule means three runs overlap behind it.
Fix: Add timeout-minutes with a realistic bound to each job.
1 further finding(s) are listed in the full report.
Checked deterministically against the repository tree and a bounded set of its files: committed credentials, unpinned actions and base images, missing lockfiles and update bots, workflows that discard failures, published advisories against the declared dependencies, runtime configuration, licensing and notebook reproducibility. No language model is involved in this section.