The software your agents are built on.
Estelle gives the coding agent you already use a lasting, grounded memory of your codebase. Context survives from session to session, every answer cites the real code behind it, and the memory learns your team's conventions as you work. Connect over MCP with one command, or point any OpenAI-compatible tool at one base URL. Your own model, your own key, always.
Get started in 60 seconds. One command detects your editors and writes the MCP config:
npx @fatelabs/estelle initWrites the config for Cursor, Cline, Windsurf, VS Code, JetBrains and Claude Code; prints the manual step for Claude Desktop and Continue. Needs a free key from /signup, no card. Prefer the API path? See Call the API.
Zero dependencies, built in CI with a signed provenance attestation, from a public source repo you can read: the package on npm · the source.
Introduction
Estelle reads your repository once and keeps it. Every file is embedded and cited to file and line. After that your agent answers from that memory instead of from whatever happened to be pasted into the window.
The memory outlives the session. Close the window, come back next week, and the context is still there. Conventions Estelle picks up from your code get applied on later runs.
Two ways in. Use either, or both.
- MCP. Add Estelle as an MCP server in the tool you already use. Your model and plan keep doing the reasoning. See Connect over MCP.
- The OpenAI-compatible API. Point any tool that can call OpenAI at one base URL and get completions grounded in your codebase. See Call the API.
You bring the model and the key. Estelle never trains on your code and never marks up a token. See BYOK and privacy.
Quickstart
A key, a command, a sweep. About a minute.
Create an Estelle key
Generate one in the dashboard. Keys look like estelle_live_…. The free tier needs no card.
Connect your editor's agent
npx @fatelabs/estelle initThis detects your installed MCP clients and writes Estelle into each config, leaving your other servers untouched. Existing files are backed up first.
Written automatically: Cursor, Cline, Windsurf, VS Code and JetBrains, plus the claude mcp add command for Claude Code. Guided, not written: Claude Desktop launches local stdio servers only, and Continue reads ~/.continue/config.yaml rather than a JSON mcpServers block. init prints the exact step for both rather than writing a file they would ignore.
It will ask for your key. Pass --key to skip the prompt. Zero dependencies, Node 18 or newer.
Ingest your repo
npx @fatelabs/estelle sweep --key $ESTELLE_KEYThis uploads the working tree into your memory. Run it again whenever you want the memory to catch up.
Restart your editor. Your agent now has find_definition, find_references, blast_radius and verify over your repo. Want the API path instead? Three values, any OpenAI-compatible client: Call the API.
Connect over MCP
Nothing about your agent changes. Same model, same base URL, same plan. The hosted server at https://api.fatelabs.ca/mcp adds grounded tools over your repository, scoped to your key. Both the endpoint and the key are in the dashboard after signup.
The quickstart command writes this config for you. To wire a client yourself:
Claude Code
Claude Code takes the server as a command instead of a config file.
claude mcp add --transport http estelle https://api.fatelabs.ca/mcp \
--header "Authorization: Bearer $ESTELLE_KEY"Any other MCP client
Paste the server into the client's MCP config.
{
"mcpServers": {
"estelle": {
"type": "http",
"url": "https://api.fatelabs.ca/mcp",
"headers": { "Authorization": "Bearer estelle_live_..." }
}
}
}Per-tool walkthroughs
Step-by-step guides for OpenCode, Cursor, Cline, Aider, Continue, and Zed live at /setup, covering both the MCP and API paths per tool.
Connect your model
Estelle does not sell tokens. Over MCP you need no provider key at all, because your existing plan does the reasoning. On the API path you bring your own key and pay your provider directly at their rate.
- Claude, by Anthropic
- OpenAI
- Kimi, by Moonshot
- GLM, by Zhipu
- DeepSeek
- Any OpenAI-compatible endpoint, including OpenRouter or a local one
Set it in Dashboard → Provider. Switch whenever you like without touching your tools: the model name stays estelle.
Authentication
A bearer token in the Authorization header, exactly like OpenAI. Send your Estelle key, not your provider key.
Authorization: Bearer estelle_live_...Key format
Production keys carry the prefix estelle_live_.
Rotating keys
Create and revoke keys in Dashboard → API keys. Revocation is immediate. Rotate on a schedule, and the moment you suspect a leak.
Your provider key
Stored separately, encrypted at rest with a key only our server holds. No endpoint returns it, so a database leak cannot expose it.
MCP tools
Once your editor is connected, your agent has these. They answer from your swept memory and the code graph built over it, scoped to your key. Your agent calls them like any other tool.
Repository navigation
The code graph. Questions an agent otherwise answers by guessing, or by reading half the repo back into its window.
Memory
Your session diary and stored decisions, from any MCP client rather than only the dashboard.
The two write verbs need the ingest permission, which member and above hold. A viewer is refused. Both preserve provenance, so a correction adds to the record rather than replacing it. See Teams and roles.
Context survival
Long sessions end when the window fills. These three move detail into durable memory and page back only what the next step needs.
Command line
The same capabilities without an editor. Zero dependencies, Node 18 or newer. Every command takes --key or reads ESTELLE_KEY from the environment.
Working with your repo
Ingest the working tree, then ask it things. recall searches memory and code together. --repo scopes it on a multi-repo account.
npx @fatelabs/estelle sweep --key $ESTELLE_KEY
npx @fatelabs/estelle ask "why does /refresh 401 after deploy?"
npx @fatelabs/estelle recall "rate limiter" --repo apiChecking code before it lands
verify checks one file for APIs that do not exist in your repo. gate runs the merge gate over your staged diff, or against a base ref. Same deterministic check as Grounding and the gate.
npx @fatelabs/estelle verify src/api/routes.py
npx @fatelabs/estelle gate --base mainEvery command
Always-on grounding
In Claude Code the check can be unconditional rather than something the agent decides to run. Hooks fire on every edit and every command.
npx @fatelabs/estelle install-hooksOpen /hooks in Claude Code, or restart it, to activate. uninstall-hooks removes only Estelle's hooks. Your own are untouched, and the MCP server is a separate switch.
Call the API
The part of the OpenAI API that coding tools use, from one base URL:
BASE https://api.fatelabs.ca/v1
- POST /v1/chat/completions sends a conversation and returns a grounded completion.
- GET /v1/models lists the models available to your key.
Any OpenAI client is three values away:
base_url = "https://api.fatelabs.ca/v1"
model = "estelle"
api_key = "estelle_live_..."Keys are created at /signup. The free tier needs no card.
curl https://api.fatelabs.ca/v1/models \
-H "Authorization: Bearer $ESTELLE_KEY"From the command line
curl https://api.fatelabs.ca/v1/chat/completions \
-H "Authorization: Bearer $ESTELLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "estelle",
"messages": [
{ "role": "user", "content": "what changed in auth last week?" }
]
}'From Python
from openai import OpenAI
client = OpenAI(
base_url="https://api.fatelabs.ca/v1",
api_key="estelle_live_...",
)
resp = client.chat.completions.create(
model="estelle",
messages=[{"role": "user", "content": "why does /refresh 401 after deploy?"}],
)
print(resp.choices[0].message.content)Chat completions
POST /v1/chat/completions
The main endpoint. Send messages, get a completion from your provider grounded in your codebase memory. Request and response shapes match OpenAI.
Request body
Example
curl https://api.fatelabs.ca/v1/chat/completions \
-H "Authorization: Bearer $ESTELLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "estelle",
"messages": [
{ "role": "user", "content": "what changed in auth last week?" }
]
}'Streaming
Set stream to true for server-sent events in the OpenAI streaming format. Existing streaming code works unchanged.
List models
GET /v1/models
Returns the models available to your key. There is one, estelle, which routes to the provider you connected. It is also the cheapest way to check your key works.
curl https://api.fatelabs.ca/v1/models \
-H "Authorization: Bearer $ESTELLE_KEY"Errors
Standard HTTP status codes, with an OpenAI-style error object.
401 means fix your credential. 402 means the credential is fine and the account needs funds. They are never interchangeable.
Rate limits
Limits scale with your plan. Exceed them and you get a 429 with a Retry-After header. Back off exponentially and honour it. Limits apply to request volume; recall itself is never metered.
How memory works
Estelle reads the repository once and embeds it. After that every request is grounded in that memory. You stop pasting files.
What persists
- The full contents of every file in your connected repositories, cited to file and line.
- Prior sessions, so context carries from one conversation to the next.
- Conventions Estelle observes in your code, applied on later runs.
Memory-tokens
Capacity is measured in memory-tokens: how much code and history Estelle keeps embedded for you. Ingest is billed once. Recall is never metered. The free tier holds one repository up to about a 2M-token codebase. Plans and overflow are on the pricing page.
Grounding and the gate
Every claim has to resolve against your real code before the answer ships. The symbol exists, takes those arguments, returns that type. A claim that cannot resolve is struck before you see it. One that passes ships with its certificate: claims cited, symbols resolved, tests green.
The check is deterministic, not statistical. In our verification eval, on a private repository no model had seen, 0% of invented APIs survived the gate. The method and the numbers behind that live in our scorecard, which we will share on request; on well-known public repos the figure is uninteresting, because a capable model has already memorised those APIs and the gate has nothing to catch.
The completeness gate
Grounding proves what the answer says is real. The completeness gate proves the reverse: it walks the symbols a change was required to touch and flags the ones the answer never reached. Every missing symbol is named and located, before the answer ships.
BYOK and privacy
Your code goes to your provider, under your terms. Estelle structurally cannot train on it. You pay your provider directly at their rate, so there is no markup on tokens; Estelle bills for the memory layer only.
What Estelle keeps
The embedded code, sessions and conventions described in How memory works. Encrypted, isolated to your team, never shared, deletable at any time.
Your provider key
Stored encrypted at rest with a key only our server holds. No endpoint ever returns it, so a database leak cannot expose it. Your Estelle key is stored as a hash only.
Your model, your plan
Over MCP, reasoning stays on the plan you already pay for. On the API path it comes from the provider you connected in Dashboard → Provider. The model stays yours either way.
Teams and roles
Each member holds one role, and each role permits a fixed set of actions. The matrix is cumulative: a role holds everything below it plus its own additions. No model is involved, and it fails closed. An unrecognised role or action is denied.
Roles manage strictly downward. An admin can change a member, never another admin or the owner.
Support
Quota questions or a deployment in your own cloud: khai@fatelabs.ca. Account and billing live in your dashboard. Reading this as an agent? The machine-readable version is at /llms-full.txt.