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
dockerfirst, thenpodman; 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_64machine — 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-existingis 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, andAGENT.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.