tugra-ai.com

Compared, with dates

How Tugra compares

Stars, commits and capability columns all read 12 September 2026 · re-measurable with the commands at the end.

Including the places where the other projects are ahead of us. Every cell says whether it comes from the project's own documents or from a function we found in its code, because those are two different kinds of evidence, and a comparison written by a vendor is worth exactly as much as its verifiability.

Read this first

Tugra is the smallest project on this page: 0 GitHub stars against claude-mem's 93,727 and Mem0's 65,183. It has no embeddings, no graph and no server; it is Markdown files in a directory behind four MCP tools. If you want a memory that fills itself and finds things by meaning, pick one of the others. Keep reading only if what you need is a memory where every record names its source, carries a verification date and a shelf life, and refuses to invent on a topic it has no evidence for.

How to read a cell. Docs is what the project's documentation says. Code names the function we found producing that behaviour at the pinned commit, or says not found when we did not find one in the files we read. Not found is not proof of absence. Nothing was executed; this is a static reading of open code and public documents. Tugra's own row is held to the same notation.

The table

Star counts, licenses and last-commit dates were read from the GitHub API on 12 September 2026. The last-commit date is the committer date of the newest commit on the default branch. Capability columns were read the same day from each project's documentation and from the source files linked under the table. If we got something wrong about your project, write to us and we will correct the page and note the correction.

ProjectStarsLast commitLicense Source required on every record Verification date and shelf life Stale records out of the default result Boundary record that forbids invention Secret pattern refused before write Per-agent permission profile Runs with no network Human-readable record file
Tugra02026-09-12Apache-2.0 YesCode: dogrula.ts requires the source list, and each entry needs a pointer and a YYYY-MM-DD date. A fact, decision or boundary without its own evidence fails gate K1 and is demoted to an observation. YesFrontmatter fields verified and shelf_life; a malformed shelf life is refused (sema.ts). Search ranks fresh above stale (arama.ts). PartlyRetired and rotten records are out of the default set. A stale record stays in, ranked below fresh ones and labelled stale with its age (arama.ts). YesBoundary is a record type (sinir, sema.ts). A proposed boundary is always quarantined until a human accepts it, whatever the proposing agent's trust (arama.ts). YesCode: olguOner() runs sirDeseniBul() over title, body and sources before anything is written; on a match nothing is written, not even the permission log, and only the pattern name is returned, never the match (arama.ts, sir.ts). Yes, when the store existsCode: yetki.ts keeps a record per agent with axes for touch, outbound, autonomy, persistence and spend ceiling, levels A0 to A5. Without a permission directory the vault runs in single-user mode and everyone can search. Yes, by constructionStdio transport (serveStdio, mcp.ts); the published modules import no http, net, dns or fetch; dependencies are the MCP SDK, yaml and zod. event_report appends a local line. Not measured with packet capture. YesOne Markdown file per record with YAML frontmatter (dosya.ts).
Mem0 OSS65,1832026-09-11Apache-2.0 NoDocs: metadata and scopes. Code: _create_memory() accepts a payload with no source (memory/main.py). Expiry onlyCode: expiration_date through _normalize_expiration_date(); no verification date. Yes, for expiredCode: search() and get_all() take show_expired=False; _payload_is_expired() filters. Nocustom_instructions exists; no dedicated boundary record. No_is_sensitive_field() is for telemetry and config; no pattern refusal on memory writes found. Scopesuser, agent and run scopes via _build_filters_and_metadata(); a scope, not an authorisation profile. NoModel and embedding providers, telemetry on by default (telemetry.py). NoVector store plus SQLite history.
Letta Code the older letta-ai/letta has 24,714 stars, last commit 2026-09-10, and its README points here3,3142026-09-12Apache-2.0 NoCode: validateMemoryFileFrontmatter() v2 requires name and description; no source field (memory-frontmatter.ts). NoNot found in the frontmatter validation. Not found Noread_only in the legacy format; no topic-ban record. Not foundDocs: secret values are hidden from context; no write-time pattern refusal found. PartlyCode: createMemoryConfinementLauncherWithAvailability() confines the memory root of memory sub-agents; permission modes in docs. PartlyLocal file paths exist; zero network across the whole harness is not stated. YesGit-tracked Markdown memory (validateMemoryTreeConstraints() processes .md).
Graphiti Zep's open layer; the hosted engine is separate30,8312026-09-11Apache-2.0 PartlyCode: EpisodicNode.source_description is required; EntityEdge.episodes=[] is allowed, so not every record (nodes.py, edges.py). Bi-temporalvalid_at, invalid_at, expired_at; not a verification date or a shelf life. Not by defaultSearchFilters time filters default to None; current-only results are not automatic. NoCustom ontology; no topic-ban semantics. Not found Scopesgroup_ids is a query scope, not an authenticated agent ACL. NoGraphiti.__init__() calls providers and telemetry; local LLM endpoints are supported. NoGraph database (Node.save()).
LangMem1,6602026-09-09MIT Nocreate_manage_memory_tool() works with default content; the schema is customisable (knowledge/tools.py). Backend's jobNo TTL on store.put() in LangMem itself. NoSearch delegates to the store; no staleness filter of its own. NoCustom schema is possible; nothing built in. Not foundNot in the manage_memory() write path. Per toolNamespaces and actions_permitted (create, update, delete) per tool; not an identity-based ACL. Backend-dependentExamples use remote models. NoIn-memory or database store (BaseStore.put()).
Cognee30,6562026-09-09Apache-2.0 PartlyProvenanceEntry.source_document defaults to an empty string (ProvenanceEntry.py). Optionalvalid_from and valid_until are optional; no mandatory verification date. Not verifiedA link from valid_until to default search exclusion was not found. No Nosanitize_value() truncates and normalises; no pattern refusal. Yes, dataset ACLcheck_permission_on_dataset(); depends on identity and permission configuration. NoDefault provider is OpenAI; local Ollama is possible; zero network not shown. NoGraph, vector and relational databases.
Official MCP memory server numbers are the whole monorepo90,2732026-09-03MIT in its README; the repository root says Apache-2.0 with remaining MIT contributions NoEntitySchema is name, type and observations only (src/memory/index.ts). No NosearchNodes() matches text and ignores age. NoFree-form observations. NocreateEntities() and addObservations() go to saveGraph() without a filter. NoOne graph manager. Yes, in its own pathStdioServerTransport and fs; no network call seen in the main path. Install and dependencies not tested. Yesmemory.jsonl.
claude-mem93,7272026-09-12Apache-2.0 Session onlyObservations are bound to a session (SessionStore.initializeSchema()); no independent source field. Nocreated_at only. NoOptional date filter in SessionSearch.searchObservations(); no default exclusion. No Opt-in tag<private> tags are removed by stripTags(); you mark it, the tool does not detect it. FiltersPlatform and project filters; per-agent authorisation not stated. PartlyLocal worker and SQLite; hosted options in some integrations; zero network not stated. NoSQLite (bun:sqlite).

Files and documents read, 12 September 2026

Where they are ahead of us

Where we are different

Not in the table, and why

Verify this page yourself

Do not take the numbers on trust. They take about a minute to gather.

# Stars, license and newest commit on the default branch
for r in dogrucanemek-alt/tugra mem0ai/mem0 letta-ai/letta-code getzep/graphiti \
         langchain-ai/langmem topoteretes/cognee modelcontextprotocol/servers thedotmack/claude-mem; do
  gh api "repos/$r" --jq '"\(.full_name)  stars \(.stargazers_count)  \(.license.spdx_id)"'
  gh api "repos/$r/commits?per_page=1" --jq '.[0].commit.committer.date'
done

# Read the same file we read, at the same commit
gh api -H "Accept: application/vnd.github.raw+json" \
  "repos/mem0ai/mem0/contents/mem0/memory/main.py?ref=c7ee362" | grep -n "show_expired"

# Try the refusal yourself: propose a fact with no source, then one with a key in it
npx tugra init
npx tugra

The second command, piped, is the MCP server. Point any MCP client at it and call fact_propose without a source: the answer names gate K1. Put a private key in the body: the answer names the pattern and writes nothing.