Contact

Quickstart: your project has a memory

By the end of this page a project on your machine has a private, durable memory it can write to, search, and correct — and so can any agent working in that directory.

Everything here runs locally. Self-hosted local mode is the supported path today; the managed API at api.enscrive.io is pre-launch and not yet serving requests.

What you need

  • Docker or Podman, plus a compose provider. The CLI probes for docker first, then podman; you do not need both, and there is no Docker shim.
  • An embedding provider key. Embeddings are computed by a provider, so a local stack still needs a key — OpenAI, Voyage, or Nebius. This page uses OpenAI.
  • A Linux x86_64 machine — see Install for the platforms published today.

1. Install the CLI

curl -fsSL https://install.enscrive.io/install.sh | sh
enscrive --version

Full detail, including what gets verified before anything lands on disk, is on the Install page.

2. Set up a self-managed profile

This writes the local stack’s configuration and fetches the service binaries from the release manifest, checking each one’s SHA256 against it as it goes.

enscrive init --mode self-managed --set-default \
  --openai-api-key "$OPENAI_API_KEY"

--set-default makes this the profile the CLI uses when you do not name one.

3. Start the stack

enscrive start

If you are on Podman

The CLI drives Podman natively. It needs the rootless user socket to be active, which is a one-time setup:

systemctl --user enable --now podman.socket

In an interactive terminal enscrive start offers to run that for you. In a non-interactive session — CI, a script, an agent — it never touches systemd; it prints that exact command and exits so you can decide. Note the --user: this is a per-user systemd unit, and no step here needs sudo.

4. Check it came up

enscrive health

5. Give this project a memory

From the root of the project you want to give a memory to:

cd ~/projects/my-project
enscrive project init --name my-project

That does four things:

  • Creates this project’s own isolated tenant on the running stack. A second project gets a second tenant and cannot see the first one’s memory. (Name collisions are refused rather than silently shared — --adopt-existing is how you deliberately opt into sharing.)
  • Stores that tenant’s API key in your per-user key store at ~/.config/enscrive/profiles.toml.
  • Drops a committable .enscrive/ marker: config.toml, which references the profile by name and carries no key material, and AGENT.md.
  • Prints the tenant it made and the portal at 127.0.0.1:3000, where you can browse the same memory in a browser.

.enscrive/AGENT.md is written for whatever coding agent works in this repository — it teaches the loop below in the agent’s own terms. Commit it. See For agents.

From here on, every enscrive command run anywhere inside this directory tree targets this project’s memory automatically. No --api-key, no --profile, no --endpoint.

6. Remember

Memories live in a corpus. corpus ensure is get-or-create and idempotent, so it is safe to run at the top of every session without checking first:

enscrive corpus ensure \
  --name "my-project-memory" \
  --embedding-model text-embedding-3-large \
  --description "Durable memory for my-project"

It prints the corpus id, and "created": true on the run that actually made it. A corpus’s embedding model is fixed at creation — if a corpus with this name already exists on a different model, the command fails loudly instead of handing back the wrong vector space.

Now write a memory:

enscrive ingest documents \
  --corpus-id <CORPUS_ID> \
  --document-id "convention/error-handling" \
  --content "Handlers return ApiError; never unwrap in a request path."

Give a memory a stable --document-id when you expect to revise it later — re-ingesting the same id replaces it. Omit it and you get a deterministic content-hash id instead, so re-ingesting identical content is a no-op. Use --content-file ./notes.md for anything longer than a sentence.

7. Recall

This is semantic search, so ask the question you actually have:

enscrive search \
  --query "how should handlers report errors" \
  --corpus <CORPUS_ID> \
  --limit 5

Always pass --corpus. You have the id from corpus ensure. The flag is declared optional, but a search that omits it currently fails.

Reading the scores

Results carry a score — cosine similarity, not a percentage. Relevant matches land nowhere near 1.0. On a measured corpus, differently-worded queries for the same fact matched at 0.57–0.70 while unrelated content sat at ≤0.39. The separation is clean, but the entire useful band is below 0.75.

So do not read a 0.6 as weak — that is what a good hit looks like. Judge by the gap between the top results and the rest, not by the absolute number. Prefer no threshold at all, which is the default; reach for --score-threshold only when you specifically want “return nothing rather than something marginal”, and start around 0.50.

8. Retire

A memory that is no longer true is worse than no memory. Delete it by id, which removes the document and all of its chunks:

enscrive corpus document delete \
  --corpus-id <CORPUS_ID> \
  --document-id "convention/error-handling"

To correct a memory rather than retire it, re-ingest the same --document-id with the new content.

Looking around

enscrive status                              # this project, its tenant, the portal
enscrive corpus list                         # every corpus in this project
enscrive corpus documents --id <CORPUS_ID>   # what is in one
enscrive corpus stats --id <CORPUS_ID>       # size and vector stats

Scripting this

Every command takes --output json and prints a predictable envelope with an ok flag, the payload, and a machine-readable failure class. That is the interface to use from a script or an agent — see For agents.

Where to go next

  • For agents — the JSON envelope, AGENT.md, and how an agent should drive this.
  • CLI reference — every command and flag, generated from the binary.
  • Concepts — corpora, voices, and environments.