Skip to content

Memory Record

Every piece of information stored in engram is a memory record. Records are stored as vectors in Qdrant and surfaced through the MCP tools. This page documents every field, its serialized JSON name, allowed values, and who sets it.

FieldJSON keyTypeSet byDescription
IDidstring (UUID)serverUnique record identifier, generated on creation
ContentcontentstringclientThe memory text; also the text that is embedded
Scopescopestringclientrun:tier:repo identifier, e.g. eval-2026-05:project:selfhosted-cluster
ReporepostringclientRepository name or URL (optional context)
WorkspaceworkspacestringclientWorkspace identifier (optional context)
Worktree pathworktree_pathstringclientPath to the git worktree (optional context)
Base dirbase_dirstringclientBase directory for the project (optional context)
SourcesourcestringclientHow the memory was produced — see Source values
CategorycategorystringclientWhat kind of memory — see Category values
Tagstagsstring[]clientFree-form labels
ActoractorstringserverVerified caller identity extracted from the OIDC token (email, username, or subject); never client-supplied; empty when auth is disabled
OwnerownerstringserverStable OIDC sub of the caller — the authorization key; never client-supplied; empty string when auth is disabled (anonymous bucket)
Visibilityvisibilitystringclient/server"" (private, default) or "shared" — see Visibility
Created atcreated_atstring (RFC3339)serverUTC timestamp of creation

The source field describes how the memory was produced. Exactly two values are accepted by the store:

ValueMeaning
user-saidThe user stated this explicitly
agent-inferredThe agent derived or inferred this

Discovery records always have source set to agent-inferred by the server.

The category field classifies what kind of memory is stored:

ValueMeaning
decisionAn architectural or design decision
preferenceA stated user or team preference
conventionA coding or workflow convention
gotchaA known pitfall or non-obvious behaviour
discoveryAgent-earned codebase understanding (see Discovery fields)

The discovery category is set by the server for records created via store_discovery; client callers use the other four values with store_memory.

The visibility field controls cross-actor reads:

ValueMeaning
"" (empty string)Private — only the owner can read and write
"shared"Readable by any authenticated caller; writable only by owner

Toggle visibility with set_visibility or update_memory’s shared argument. Sharing grants read only — another actor can never write a record they do not own, even when it is shared.


Records in the discovery category carry additional fields that are absent (or zero-valued) on regular memory records.

FieldJSON keyTypeRequiredDescription
Kindkindstringyesmap (orientation/structure) or fact (pinned checkable claim)
CitationscitationsCitation[]yesAt least one source anchor; max 50
SummarysummarystringnoShort human-readable summary

Discovery records live in scopes starting with discovery:, typically discovery:repo:<repo>. They are recalled on demand via search_discovery and are never returned by list_memory session bootstrap.

Each citation anchors a discovery claim to a verifiable source:

FieldJSON keyTypeRequiredDescription
Kindkindstringyesfile, commit, url, or repo
RefrefstringyesPath, repo URL, or doc URL
LocatorlocatorstringnoE.g. 200-240 line range
PinpinstringnoAging anchor: commit SHA, content-hash, @rev, or fetched-at
ExcerptexcerptstringnoCached substance from the source; max 16 KiB, soft cap ~50 lines

The serialized JSON keys match the Go struct tags in internal/store/store.go exactly:

  • worktree_path (not worktree) — the struct field is Worktree string \json:“worktree_path”“
  • base_dir (not baseDir) — snake_case throughout
  • created_at — RFC3339 string in the Qdrant payload; deserialized as time.Time

These match the README’s description (repo/workspace/worktree_path/base_dir).


actor and owner are always server-set. The actor is extracted from the token’s UserID (email, username, or subject claim, in priority order). The owner is always the stable OIDC sub claim — it does not change when the user’s email changes.

When authentication is disabled (no --oidc-issuer), both actor and owner are empty strings, and all callers share one anonymous bucket.

Pre-isolation records — those written before per-actor ownership was added — carry no owner key (distinct from an empty-string owner). They are invisible to every owner-scoped read. See Auth and Isolation for migration details.