Skip to content

System map

This is the map of the whole engine, and the first page to read before you open any other developer page. It assumes you know what a scope is and that you can read SQL. Everything below points at a real module or a real container, so you can check any claim against the tree yourself.

How aizk fits together, from an agent through the store and back to evidencekeepfindprojectscopesMCP clientLogto identitywrite pathread pathgate → extract → groundone SQL storeautonomous passessourced evidence

Three roles with profile-specific services

Section titled “Three roles with profile-specific services”

All AIZK code ships as one package with three roles. Compose runs each as its own container. The AWS profile serves the browser API and MCP surface from one public Lambda and runs the worker in a second private Lambda.

Process Command What it owns
MCP server aizk admin server mcp the agent-facing tool surface
Browser API aizk admin server api the JSON API the web app calls
Worker aizk admin server worker the queue drain and the scheduled passes

Each one calls Runtime.assemble(settings) in src/aizk/runtime.py exactly once, which builds the database handle, the byte store, the artifact services, the Logto client, and the four model clients for embedding, reranking, gating, and generation. Nothing else in the codebase constructs those. Design principles explains why that rule exists and where it is bent.

The surrounding services depend on the selected profile. The self-hosted profile uses PostgreSQL with VectorChord, SeaweedFS, ClamAV, Docling and local model services. The AWS profile uses CockroachDB Cloud, C-SPANN, S3 and hosted model endpoints. Logto provides identity in both. Database profiles owns the comparison, while Deployment topology owns the Compose service list.

A keep call lands on Memory.keep in src/aizk/memory.py, the one service both the MCP server and the browser API share. Plain text and a file take different routes from there.

caller
│ keep
Memory.keep
├── text ──────────▶ extract.ingest ──┐
│ │
└── uri or file ──▶ ArtifactIntake │
│ scan, then │
│ convert │
▼ │
Markdown ───────────┤
document and chunks
│ enqueue
durable queue
│ worker
gate ─▶ extract ─▶ ground ─▶ consolidate
entities and facts

Text goes straight to extract.ingest.ingest_text, which writes the document and its chunks and embeds them. A URI or an upload goes to the artifact intake first, which scans the bytes before storing them and converts them to Markdown only after the scan passes, then feeds the Markdown back into the same ingest path. Intake and Artifacts cover both.

The transaction that writes the document ends there. enqueue_document puts a job in the active durable queue. PgQueuer owns that work on PostgreSQL, while AIZK queue tables and the portable worker own it on CockroachDB. A caller never waits for graph extraction. Extraction and the gate covers the job, and The job system covers both queue implementations.

A find call is synchronous and touches no queue. Memory.find calls retrieval.find, which runs the lanes in src/aizk/retrieval/lanes/, fuses them, reranks the survivors with a cross-encoder, and packs the highest-merit prefix that fits the token budget. The result is rendered from src/aizk/retrieval/templates/find.md.j2 into one prompt-ready Markdown string.

The retrieval plan decides which lanes run. The full PostgreSQL plan can run every lane and use a cross-encoder to order the fused candidates. The bounded AIZK plan disables measured high-cost lanes and can skip reranking. How find runs owns the shared path, and Retrieval tuning owns the switches.

aizk stores no users, no organizations, and no memberships. src/aizk/auth.py verifies the bearer token against Logto, and User.authorized in src/aizk/store/identity/user.py derives a stable UUID5 identity and the caller’s organization standing from the verified claims alone.

bearer token ──▶ Auth.verify_token ──▶ claims
User.authorized(...)
┌────────────────────────────┴───────────────┐
▼ ▼
app.orgs (readable) app.writable_orgs
└──────────────┬─────────────────────────────┘
every protected row policy reads these

User is an rls.Context subclass with the prefix app. The PostgreSQL adapter binds those settings directly. The CockroachDB adapter carries the same authority through a transaction-local connection setting that its policies parse. The application role cannot bypass row security, so code that forgets to filter returns nothing instead of returning everything. The Logto boundary and Row level security go deeper.

Background work has no bearer token, so it uses User.system() or User.private(user_id) instead, which is a separate and deliberately narrow door. Background work explains what each one may see.

The MCP server and the browser API are thin. Neither builds a SQL statement and neither opens a session, which is enforced by src/aizk/mcp/ruff.toml and src/aizk/api/ruff.toml banning sqlmodel.select, the session classes, and Database inside those two packages. Both reach the store through model classmethods and User.exec. Layers and import contracts has the wider version of that rule.