GitLoom

Namespaces and multi-user patterns

One namespace is one memory. How to map your users onto them, and what happens at the edges.

A namespace is one memory: its own git repository, its own index, its own vectors. Two namespaces are two repositories under two different storage prefixes, so isolation is a storage boundary rather than a WHERE clause somebody can forget to write.

An account has many namespaces. A credential resolves to an account, never to a namespace — which namespace a request touches is decided by your server, per request.

The rules

Creating and listing

curl -s -X POST "$GITLOOM_API/v1/namespaces" \
  -H "Authorization: Bearer $GITLOOM_KEY" \
  -H "content-type: application/json" \
  -d '{"namespace":"user-8213"}'
{"namespace":"user-8213","created":true}

A second call:

{"namespace":"user-8213","created":false}
```bash curl -s "$GITLOOM_API/v1/namespaces" -H "Authorization: Bearer $GITLOOM_KEY" ```
{"namespaces":["default","user-8213"]}

The listing returns the first 200 namespaces and truncates silently. There is no pagination and no count. If you expect more than 200, do not use this endpoint as your source of truth — your own user table already knows which namespaces exist.

Pattern 1: one namespace per end user

The default, and the shape the isolation model was built for.

const ns = `user-${user.id}`

await gl("/v1/namespaces", { method: "POST", body: JSON.stringify({ namespace: ns }) })

Why per-user rather than one shared memory with a user tag:

Deriving the name deterministically from your own user id is the important part. Do not store the namespace name as a separate mutable field, and do not use anything the user controls — an email address is not [a-z0-9-].

// Safe: your own opaque id, prefixed so it can never collide with an internal name.
const ns = `user-${user.id}`

// Unsafe: user-supplied, wrong charset, and a 400 you will discover in production.
const ns = user.email

If your ids are UUIDs, they already fit the charset once lowercased. If they are integers, prefix them — a bare 8213 is legal but tells you nothing in a log line.

Creating it at the right moment

Create the namespace when you create the user, not on the first write. The write path checks existence at the front door and returns 404 while you are still listening, so the failure is loud — but recovering from it mid-conversation means the turn the user just had is the one you lose.

Creation is idempotent and cheap, so the belt-and-braces version is fine too:

try {
  await memory.remember(turns, { namespace: ns })
} catch (e) {
  if (e instanceof GitloomError && e.isNamespaceNotFound) {
    await memory.createNamespace(ns)
    await memory.remember(turns, { namespace: ns })
  } else throw e
}

Pattern 2: one namespace per project or workspace

When the memory belongs to a team rather than a person — a shared assistant in a Slack channel, a per-repository code agent — name the namespace after the workspace:

account "acme"
├── workspace-42     ← shared by everyone in that workspace
├── workspace-91
└── default

The tradeoff is that everything one member tells the assistant is retrievable by every other member. That is usually the intent for a shared bot; it is never the intent for a consumer product.

Pattern 3: one namespace, single-user

A personal tool, a single-tenant internal agent. Create default, never send a namespace parameter, and stop thinking about it.

curl -s -X POST "$GITLOOM_API/v1/namespaces" \
  -H "Authorization: Bearer $GITLOOM_KEY" \
  -H "content-type: application/json" -d '{}'

Anti-pattern: a namespace per conversation

Do not do this. A namespace per session throws away the thing GitLoom is for — a memory is valuable precisely because it spans sessions. Use session_id on the write to tag which conversation a memory came from; the namespace is the person, not the chat.

What isolation does and does not give you

Does: one namespace's retrieval cannot see another's content, at the storage layer. A container caches up to four warm namespaces at once and keys that cache by the exact account-and-namespace pair, so a shared Lambda container cannot cross the line either.

Does not: protect you from your own routing bug. An API key is account-wide. If your server passes the wrong namespace for the request it is handling, GitLoom will answer faithfully with the wrong user's memories. The namespace should be derived from the authenticated session on every request, in one place, and never accepted from a request parameter your client controls.

// Good: derived server-side from the session.
const ns = `user-${session.userId}`

// Catastrophic: the client chooses whose memory to read.
const ns = req.query.namespace

Quotas are per account, not per namespace

The Free plan's 100 writes / 2,500 reads / 1,000 memories / 25 playground chats per month are counted across every namespace in the account. Adding users does not add allowance — see Plans and pricing for every tier, and for how paid plans overflow into a wallet instead of stopping. Check where you stand:

curl -s "$GITLOOM_API/v1/usage" -H "Authorization: Bearer $GITLOOM_KEY"
{
  "period": "2026-07",
  "plan": "free",
  "used": {"writes": 12, "reads": 340, "memories": 87, "chats": 3},
  "limits": {"writes": 100, "reads": 2500, "memories": 1000, "chats": 25},
  "exceeded": false
}

Counters are month-keyed, so a new month starts from zero with no reset to wait for (memories is the one exception — it is a level, not a flow, so it is not scoped to a period). Exceeding a meter on Free or pay-as-you-go returns 429 quota_exceeded on the affected route; a subscribed plan overflows into the wallet instead — see Overage.

Multi-tenant SaaS: one account or many?

If you are building a product whose customers each have many end users, the natural shape is your GitLoom account : your customers : their users = 1 : n : m, with one namespace per end user and a naming convention that encodes both levels:

c42-u8213
c42-u9007
c91-u1004

A single account means one credential to manage and one quota to watch. It also means one blast radius: a leaked key exposes every customer. Separate GitLoom accounts per customer give a real credential boundary, at the cost of managing signups and quotas per customer — and there is no API for provisioning accounts today, so that path is manual. For most launches, one account with a strict server-side namespace derivation is the right call.