> For the complete documentation index, see [llms.txt](https://docs.gitloom.cloud/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gitloom.cloud/documentation/content/concepts.md).

# Core concepts

Five ideas explain the whole API. Four of them are nouns; the fifth is the decision everything else follows from.

### Account

An account is who pays and who owns the data. It is created when you sign up, and it is the subject of every credential: an API key resolves to exactly one account, and so does a dashboard session.

An account id is a short slug — lowercase letters, digits and `-`, up to 64 characters. That charset is a security boundary, not a style rule: storage keys are built by joining segments with `#` and `/`, so an id containing either could address another account's data. Restricting the alphabet makes that unrepresentable rather than merely unlikely.

You see your account id in `GET /v1/whoami`, and as `tenant` in a retrieval response.

### Namespace

A namespace is **one memory**. It has its own git repository, its own index, and its own vectors.

The two levels exist because one paying account has many end users, and each needs a memory that cannot see the others'. Isolation here is a *storage boundary*, not a `WHERE` clause somebody can forget to write: two namespaces are two different repositories in two different key prefixes, so a bug in query construction cannot leak one into the other.

```
account "acme"
├── namespace "user-8213"   ← its own repo, index, vectors
├── namespace "user-9007"   ← its own repo, index, vectors
└── namespace "default"
```

The recommended pattern is **one namespace per end user**. Nothing enforces that — an account may legitimately want one shared memory, or one per project — but per-user is the shape the isolation model was built for.

Two rules worth internalising:

* **Namespaces are created explicitly.** A write to an unknown namespace is `404`. Implicit creation would turn a typo in a user id into a new empty memory that reports success, and you would spend an afternoon wondering where the data went.
* **`default` is a name, not a magic namespace.** Omitting `namespace` on a request means `default`, and `default` must have been created like any other.

Writes to one namespace are serialised; writes to different namespaces run in parallel. That is a property of the queue, not a lock — see [Limits and behaviour](https://github.com/MelloB1989/gitloom/tree/main/limits/README.md).

### Memory

A memory is a markdown file with a YAML header. That is the whole storage format: there is no hidden binary representation, and everything the system knows is readable by a person.

```markdown
---
tier: facts
tags: [camera, purchase]
created: 2026-07-31T00:00:00Z
updated: 2026-07-31T00:00:00Z
confidence: 0.9
cues:
  - what camera do I own
  - Sony A7III purchase
  - my photography gear
  - mirrorless camera body
related:
  - same-purchase: facts/camera-gear/kit-lens-28-70-9a01c4de.md
---

Bought a Sony A7III on 2026-07-31 from Fotocentre in Bengaluru for ₹142,000,
chosen over the A7IV on price.
```

Memories are **atomic**: one fact per file. Two purchases are two memories even though they are the same kind of thing, because merging them makes "how many" questions unanswerable. Files are filed by tier and topic, so a path looks like `facts/camera-gear/bought-a-sony-a7iii-1f4b9c2a.md` — the trailing hex is a content hash that keeps two same-titled facts from colliding.

#### Tiers

The first path segment is the tier, and it decides how a memory ages.

| tier        | what it holds                                                                              | lifetime                                                              |
| ----------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `facts`     | stable knowledge about the user — identity, possessions, preferences, plans, relationships | indefinite, rewritten in place when it changes                        |
| `incidents` | dated events                                                                               | may carry a `ttl`; expired incidents drop out of retrieval by default |
| `rules`     | small standing instructions                                                                | indefinite, small enough to load whole                                |
| `skills`    | procedural know-how                                                                        | indefinite, loaded lazily                                             |

Today's ingestion pipeline writes almost entirely into `facts`. The other tiers are part of the format and the retrieval filters, and are populated as the extraction prompts learn to distinguish them.

#### You do not write memories — you write conversations

`POST /v1/memories` takes a transcript. A model reads it and produces the memories. That is deliberate: the useful unit for an application is "here is what just happened", and the judgement about what is worth keeping, how to phrase it, and whether it replaces something already stored is exactly the part you do not want to reimplement.

Extraction runs in stages:

1. **Extract** — pull every distinct fact worth remembering, atomically, preserving structure (a shift roster stays a roster; the pairings are usually the thing that gets asked about later).
2. **Cue** — write the retrieval keys (below).
3. **Relate** — link facts that concern the same real-world entity or event.
4. **Reconcile** — decide, for each new fact, whether an existing memory is about *the same subject*. If so the file is **rewritten** to state the current truth while preserving every earlier specific the new fact does not contradict. If not, a new file is created — which is the default, and deliberately so.

Reconciliation is why the store holds one file per real-world subject rather than a growing pile of near-duplicates. It is also the only stage that must run in session order, which is why writes to one namespace serialise.

### Cue

A cue is a short, question-shaped retrieval key, written at ingestion time, describing what somebody would **ask** to find this memory.

```yaml
cues:
  - what camera do I own
  - Sony A7III purchase
  - my photography gear
  - mirrorless camera body
```

**Cues are what get embedded — not the memory body.** This is the least obvious design decision in GitLoom and the one that most affects retrieval quality, for two reasons:

* **The vector space is aligned with questions, not with prose.** A user asks "what camera do I own"; the memory says "Bought a Sony A7III on 2026-07-31 from Fotocentre". Those are not close in embedding space. The cue is, because the cue was written in the shape of the question.
* **The vector table stays small.** A handful of cues per memory, rather than one vector per chunk of text. Semantic search scans a table sized by facts, not by characters.

Each cue resolves back to its file, so a cue match returns the whole memory.

A good cue carries a distinguishing term — a name, brand, place, number or specific activity. `"Sony A7III purchase"` is a cue; `"camera"` is noise, because a cue that could match dozens of memories makes this memory surface for unrelated questions. The extraction prompt is explicit about this, because the vague version measured worse.

### Retrieval: three arms, fused

`GET /v1/retrieve` makes **no model call except the query embedding**. It runs three searches concurrently and fuses them:

* **Lexical (BM25)** finds exact wording — names, model numbers, quoted phrases.
* **Semantic (cue vectors)** finds the same meaning in different words.
* **Graph (one hop)** finds memories *connected* to a hit without resembling the query at all: a trip's hotel, a person's employer, an item and where it was bought. This is what multi-hop and "how many" questions need, and neither of the other two arms can produce it.

The three are complementary by construction, and the results are combined by reciprocal-rank fusion — which is why `score` is a rank artefact rather than a similarity, and only comparable within one response.

The output is evidence, not an answer. The intended shape is: retrieve in milliseconds, then hand the snippets to whatever model is already talking to your user. Keeping the answering model out of the read path is what makes retrieval fast and its cost predictable.

### Why git

The store is a real git repository per namespace — commits, history, `git log`, the lot. Three things follow, and they are the reason for the choice:

**Memory is current-state, and history is free.** A fact that changes gets its file rewritten, not appended to. The memory always states what is true *now*, which is what a retrieval should return — and the previous version is still in the object store, because that is what a commit is. You get correction without losing the record of what was corrected, and you did not have to build a versioning scheme to get it.

**Change detection is native.** Every node in the index is keyed by its git tree or blob hash. Working out what to re-index after a write is a tree diff, not a scan: a directory whose tree hash is unchanged has no changed descendants, by construction. That is what keeps incremental ingestion proportional to what actually changed.

**It is inspectable and portable.** The memory of a user is a directory of markdown you can read, diff, grep, and hand back to them. Nothing is trapped in a schema. When somebody asks *why does the assistant think that about me*, the answer is a file and a commit, not a vector you cannot interpret.

The repository is packed and stored in object storage, one per namespace, and updated with a compare-and-swap so two writers cannot silently lose each other's work.

### Two credentials, one API

Both an API key and a dashboard session reach exactly the same endpoints. That is deliberate: a developer and the dashboard call the same API, which is what makes the examples in these docs true for everyone rather than true for one of them.

The single exception is key management. `/v1/keys` refuses API keys with `403 dashboard_only`, because a leaked key that can mint its own replacements survives the revocation meant to contain it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.gitloom.cloud/documentation/content/concepts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
