{
  "markdown": "<p align=\"center\">\n  <img width=\"1024\" alt=\"Leteo — living memory for AI agents. Light. Local. Yours.\"\n       src=\"assets/branding/leteo-banner.png\" />\n</p>\n\n<p align=\"center\">\n  <a href=\"#install\">Install</a> &bull;\n  <a href=\"#what-it-feels-like\">How it works</a> &bull;\n  <a href=\"#what-you-type\">Commands</a> &bull;\n  <a href=\"#languages\">Languages</a> &bull;\n  <a href=\"openspec/\">Specs</a>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/asanabrial/leteo/actions/workflows/ci.yml\"><img alt=\"CI\" src=\"https://github.com/asanabrial/leteo/actions/workflows/ci.yml/badge.svg\" /></a>\n  <a href=\"LICENSE\"><img alt=\"License: MIT\" src=\"https://img.shields.io/badge/License-MIT-blue.svg\" /></a>\n  <a href=\"https://www.rust-lang.org\"><img alt=\"Rust 1.97+\" src=\"https://img.shields.io/badge/rust-1.97%2B-orange.svg\" /></a>\n  <a href=\"#mcp\"><img alt=\"MCP: 22 tools\" src=\"https://img.shields.io/badge/MCP-22%20tools-purple.svg\" /></a>\n  <a href=\"#install\"><img alt=\"Linux, macOS, Windows\" src=\"https://img.shields.io/badge/platforms-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey.svg\" /></a>\n</p>\n\n---\n\n# Leteo — persistent memory for AI coding agents\n\nYour coding agent forgets everything when the session ends, and most of it when\nthe context is compacted. Leteo is the memory it keeps: decisions, bug fixes,\nconventions and the discoveries that were expensive to make, stored in a local\nSQLite database and handed back when they are relevant.\n\nOne binary, no server, no API key. Nothing leaves your machine unless you turn\non cloud replication for a project you name.\n\n**Measured against itself** — the same agent, the same questions, once with it\nand once without:\n\n<p align=\"center\">\n  <img width=\"880\" alt=\"Tokens per run on the one question the code cannot answer. Without Leteo: 3 runs at 27,394, 85,950, 98,733 tokens, 0 of 3 right. With Leteo: 4 runs at 29,996*, 55,189, 71,037, 100,417 tokens, 4 of 4 right. The median falls from 85,950 to 63,113, which is 27% fewer tokens. The best run saves 70% and the median 27%. The run marked * is off-protocol: its prompt asked for one field the others did not, and dropping it leaves the best run saving 44%, the median 17%, and 3 of 3 right against 0 of 3.\" src=\"assets/tokens.svg\" />\n</p>\n\n**Four right answers out of four, where the agent without it got none out of\nthree.** That is the finding that survives the variance, and the tokens are the\nnoisier half of it. [Every run is here](docs/does-memory-save-tokens.md) — both\nbaselines, the fixed cost of about 15,400 tokens a session that **both arms\npay**, and the pairing from these same seven runs that says Leteo *costs 3.7x as\nmuch*.\n\n**Works with:** Claude Code · ZCode · Codex · Cursor · Gemini CLI · OpenCode · Windsurf ·\nVS Code Copilot · Kilo Code · Qwen · Kiro · Antigravity · Pi · DeepSeek Harness\n\n## What it feels like\n\nYou never prompt it to remember. That is the whole idea. Your agent opens each\nsession already holding what the project knows, and saves as it goes while you\nwork — a bug fixed, a convention agreed, something non-obvious learned. Those\nnotes are written for its future self rather than for you, so they stay out of\nthe conversation.\n\n![A terminal: a new session asks what this project knows and gets back three memories an agent saved on its own — a connection pool that runs out at 20 workers, money kept as integer cents, Stripe retrying webhooks three times — then searches them mid-task](assets/leteo-loop.gif)\n\nThe rest is a SQLite file you own. `leteo tui` opens it, `leteo export` takes it\nwith you, and `leteo delete` means it.\n\n![The Leteo dashboard in a terminal: eleven memories across two projects, narrowed to one by typing \"connection pool\", then opened to show the whole memory](assets/leteo-tui.gif)\n\n## Does it pay for itself?\n\nMeasured rather than asserted, over thirteen runs: **four right answers out of\nfour on the questions the code cannot answer, where an agent without it got none\nout of three** — or three out of three against that same none out of three, on\nthe strict protocol. That is the finding that survives the variance.\n\nThe tokens are noisier. On those same questions the median saving is 27% and the\nbest case 70% — or 17% and 44% if you drop the one run whose prompt differed by\na field, which the article does for you. On questions the code *does* answer the\ndifference is inside the noise, and the same thirteen runs support *\"costs 3.7x\nas much\"* if you pick the opposite pair.\n\n[*does memory save tokens?*](docs/does-memory-save-tokens.md) shows every run,\nboth baselines, the fixed per-session cost of about 15,400 tokens, and where the\nnumber is weak. The honest summary is that it does not reliably save you tokens\n— it stops your agent confidently answering something else.\n\n## Install\n\n```bash\n# Linux and macOS\ncurl -fsSL https://raw.githubusercontent.com/asanabrial/leteo/main/scripts/install.sh | sh\n```\n\n```powershell\n# Windows\nirm https://raw.githubusercontent.com/asanabrial/leteo/main/scripts/install.ps1 | iex\n```\n\nOr through a package manager, if you already keep your tools in one. Homebrew\ncovers macOS and Linux, on both architectures:\n\n```sh\nbrew tap asanabrial/leteo && brew install leteo\n```\n\n```powershell\n# Windows\nscoop bucket add leteo https://github.com/asanabrial/scoop-leteo\nscoop install leteo\n```\n\n```sh\n# Anywhere with Node, if that is what you already have\nnpm install -g @asanabrial/leteo\n```\n\nThat last one puts `leteo` on your `PATH` for the command line, and is the one\nroute that cannot configure an agent: `leteo setup` refuses from a binary npm\nis holding, because the path it would write down is one npm deletes. Configure\nthe agent with [the `npx` line](#without-installing-anything) instead, or\ninstall by any of the routes above.\n\nThen open Leteo and set your agent up from the Setup screen:\n\n```bash\nleteo tui\n```\n\nThat is all of it. Nothing else to install first — not Rust, not SQLite, not a\nruntime: the archives are prebuilt binaries with SQLite compiled in, and each\nscript checks its download against the published `SHA256SUMS` before installing\nanything. From the scripts the binary lands in `~/.local/bin`, or\n`%LOCALAPPDATA%\\leteo\\bin` on Windows — Homebrew and Scoop put it where they\nput everything else. `LETEO_INSTALL_DIR` moves that, `LETEO_VERSION` takes a\ntag other than the latest release, and `LETEO_BASE_URL` downloads from\nsomewhere other than GitHub releases. Those three belong to the scripts rather\nthan to the binary, which is why they are not in the\n[Environment](#environment) table.\n\nReleases carry five builds — x86-64 Linux, Windows and macOS, and arm64 Linux\nand macOS. The two Linux ones ask for nothing newer than glibc 2.34, so they\nrun on Debian 12, Ubuntu 22.04, RHEL 9 and anything later. That floor is pinned\nin the release workflow rather than inherited from whichever image GitHub calls\nlatest: v0.1.0 inherited it, and wanted a glibc newer than Debian stable's.\nOn anything else, build from source, which is the one route that needs Rust:\n\n```bash\ncargo install leteo\n```\n\nThat builds the released version from [crates.io](https://crates.io/crates/leteo).\n`cargo binstall leteo` fetches the same release binary instead of compiling it,\nwhich is the faster half of that sentence. To build whatever is on `main`\ninstead, including work that has not been released yet, ask for the repository:\n`cargo install --git https://github.com/asanabrial/leteo`.\n\n### Without installing anything\n\nMost MCP documentation assumes `npx`, so there is a wrapper on npm that fetches\nthe release binary for your platform, checks it against the same published\n`SHA256SUMS`, and hands it every argument:\n\n```json\n{\n  \"mcpServers\": {\n    \"leteo\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@asanabrial/leteo\", \"mcp\"]\n    }\n  }\n}\n```\n\n`bunx @asanabrial/leteo mcp` works the same way — it is the same package from\nthe same registry, and the wrapper depends on nothing but what both runtimes\nalready have. `npm install -g` above is that same package installed once\ninstead of fetched per run.\n\nThe npm version *is* the release tag, so pinning one in npm pins the binary it\nfetches — a guard holds the two numbers together, because published one behind\nit would quietly serve the previous release to everybody arriving this way. It\nis a way in rather than the way to run it: a binary on your `PATH` starts\nwithout a download and is what `leteo setup` writes into your agent.\n\n### As a plugin\n\nClaude Code takes Leteo as a plugin, which registers the same MCP entry and the\nsame five lifecycle hooks `leteo setup` writes, through Claude Code's own plugin\nmachinery:\n\n```text\n/plugin marketplace add asanabrial/leteo\n/plugin install leteo@leteo\n```\n\nThe plugin carries configuration, not the binary. Install `leteo` by one of the\nroutes above first: the MCP entry and every hook the plugin registers run\n`leteo` from `PATH`, so without it they are five commands that are not there.\nWhat the plugin replaces is the setup step, and removing it takes those entries\naway again.\n\nPick one of the two, not both. Registered twice, every lifecycle event runs\ntwice — which stored each prompt twice, 23 identical pairs on the machine where\nit was found, before anybody noticed anything was wrong. `leteo setup --hooks`\nnow looks for an installed bundle and refuses rather than adding a second\nregistration, naming the file it found.\n\nCodex has the same bundle under [`plugin/codex`](plugin/codex), and there it is\nthe only route to the hooks — `leteo setup codex` registers the MCP server and\nno hooks at all, which leaves Codex holding the tools with nothing telling it\nwhen to reach for them.\n\nZCode has one under [`plugin/zcode`](plugin/zcode), from the same marketplace.\nAdding a marketplace is a desktop action, not a command: **Settings → Plugins →\nCreate → Add marketplace**, pointing at `asanabrial/leteo`. Then install\n`leteo-zcode` from the list.\n\nIts CLI does not add marketplaces — `zcode plugins` lists and enables what is\nalready installed, and nothing more. Checked against `zcode 0.16.5`:\n\n```text\n$ zcode plugins --help\n  plugins    List and enable installed plugins (`plugins list`)\n```\n\nIt registers three hooks rather than five, because that client fires neither\n`SubagentStop` nor `SessionEnd`. It is also the route that does not depend on\nsomebody else's switch: ZCode runs configuration-file hooks only while\n`hooks.enabled` is true in `~/.zcode/cli/config.json`, which starts off, and\nenabling the plugin is what enables the plugin's hooks.\n\n## Uninstall\n\n```powershell\nleteo uninstall\nleteo uninstall --yes\n```\n\nThe first reports what would go and changes nothing. The second carries it out:\nLeteo leaves every agent it configured, and then the machine. On Windows it also\nregisters itself in Installed apps, so it can be removed from there instead.\n\nTo leave one agent and stay in the rest:\n\n```powershell\nleteo setup claude-code --uninstall\n```\n\nThat takes out the MCP entry, the lifecycle hooks and the protocol block, and\nnothing else — other servers, other tools' hooks and your own notes stay where\nthey are.\n\n## MCP\n\nA 22-tool MCP server over standard input/output, run directly or written into a\nclient by `leteo setup`:\n\n```powershell\nleteo mcp\n```\n\nRun like that it offers all of them. Three of the twenty-two change or count\nthe whole store, and `leteo setup` leaves those out: what it writes into an\nagent is `--tools=agent`, the nineteen an agent reaches for while it works.\n`--tools` picks a profile or a single tool, and `--project` fixes the project\nfor the process.\n\nAlongside it there is a JSON command line, an interactive terminal UI, and\nsetup support for fourteen MCP clients — the list at the top of this page.\n\nIts name in the [MCP Registry](https://registry.modelcontextprotocol.io) is\n`mcp-name: io.github.asanabrial/leteo`, which is what [`server.json`](server.json)\npublishes. The line is written out rather than hidden in a comment because that\nis what the registry reads to believe this repository owns the crate, and\ncrates.io strips HTML comments when it renders this file.\n\n## What you type\n\nRarely anything: the saving and the recalling happen without you. This is the\nstore from the outside, for the times you want to look yourself. Every command\nthat answers prints JSON — `tui` is the exception, being a screen rather than an\nanswer — and the default database is `~/.leteo/leteo.db`.\n\n**Reading it.** `search` is the one you will actually use, and `--all-projects`\nwidens it past the project you are standing in. `recent` is the last few in time\norder; `context` is the block an agent is handed when a session opens, so it\nshows what yours are starting with; `timeline` reads what was saved either side\nof one memory; `stats` counts what is there. `tui` is all of it on one screen.\n\n```powershell\nleteo search \"connection pool\" --project leteo\nleteo search \"connection pool\" --all-projects\nleteo recent --project leteo --limit 20\nleteo context leteo --scope project\nleteo timeline 42 --before 5 --after 5\nleteo stats\nleteo tui\n```\n\n**Writing by hand.** Seldom needed, since the agent saves as it works — but a\nmemory you want in your own words, and the session boundaries an agent would\notherwise draw for you:\n\n```powershell\nleteo save \"SQLite architecture\" \"One writer, many readers\" --project leteo --type architecture\nleteo session-start session-1 --project leteo\nleteo session-end session-1\n```\n\n**Setting an agent up.** On its own it walks through it; naming an agent does\nthat one. `--hooks` adds the lifecycle hooks that make memory automatic, and\n`--dry-run` reports every file it would touch without writing one:\n\n```powershell\nleteo setup\nleteo setup claude-code --hooks\nleteo setup opencode --dry-run\n```\n\n**Keeping it well.** `doctor` runs every check and says which one failed and\nwhy; `--repair` carries out the three that are safe to make on their own —\nrestoring missing full-text triggers, rebuilding the indexes, and recomputing\nstale hashes.\n`export` and `import` move a store between machines, and `obsidian-export`\nwrites it into a vault as Markdown:\n\n```powershell\nleteo doctor\nleteo doctor --repair\nleteo export --project leteo --output leteo-export.json\nleteo import leteo-export.json\nleteo obsidian-export --vault C:\\Vaults\\Notes --project leteo\n```\n\n**Projects.** A project is worked out from the directory, so the same work can\nend up filed under two names. `consolidate` folds a group of them into one name,\n`prune` drops the ones holding no memories at all:\n\n```powershell\nleteo projects list\nleteo projects consolidate --project leteo --apply\nleteo projects prune --apply\n```\n\n**Conflicts.** When a new memory looks like it contradicts an older one the two\nare paired and the agent settles the pair. These read the same pairs from\noutside: `list` and `show` for what is there, `scan` to look for pairs nobody\nhas recorded yet, `stats` to count them by verdict:\n\n```powershell\nleteo conflicts list --project leteo --status pending\nleteo conflicts show 7\nleteo conflicts scan --project leteo --apply\nleteo conflicts stats --project leteo\n```\n\n**Deleting.** Without `--hard` a memory is marked deleted and stops coming back\nin answers; with it, the row is gone and its relations are cut. A project takes\nthe same flag. A session takes none, and while it still holds memories deleting\nit is refused outright and says how many — a session goes when it is empty, not\nby taking its memories with it:\n\n```powershell\nleteo delete observation 42 --hard\nleteo delete session session-1\nleteo delete project leteo --hard\n```\n\n`projects consolidate`, `projects prune` and `conflicts scan` change nothing\nuntil `--apply`: without it each one reports exactly what it would do.\n\n## Languages\n\nThree settings, because they answer three different questions.\n\n**`interface`** is Leteo's own screens: the panels, the menus, the help. Twelve\nlanguages — English, español, português, français, Deutsch, italiano, català,\ngalego, euskara, Nederlands, polski, svenska — deliberately the same twelve\noffered for memories, from the same table. Left unset it follows the machine's\nlocale, so a Spanish computer gets a Spanish dashboard without being asked.\n\n**`voice_language`** is what Sardi speaks, and it is separate because those\nlines are written *into your agent's conversation* rather than onto Leteo's\nscreens. Working in English on a Spanish machine is an ordinary thing to do.\nLeft unset it follows `interface`. It is the same twelve languages.\n\n**`language`** is what memories are written in. It is handed to a model rather\nthan parsed, so it is free text and not limited to the twelve above: `español`,\n`Spanish`, `português do Brasil` and `日本語` all work. Left unset, each memory\nis written in the language of the conversation that produced it.\n\n## Settings\n\nThose three and two more are kept in `settings.json`, in the data directory —\n`~/.leteo/settings.json` unless you moved it. The Setup screen writes the file,\nand it is also meant to be opened by hand: a value it cannot read costs that one\nsetting rather than the whole file. Nothing says so at the time, though, because\na hook must not fail while you are mid-edit — `leteo doctor` is what names a\nsetting being read past.\n\n| Key | Values | Unset means |\n| --- | --- | --- |\n| `interface` | one of the twelve above | follow the machine's locale |\n| `voice_language` | one of the twelve above | follow `interface` |\n| `language` | free text | the language of each conversation |\n| `voice` | `all`, `reminders`, `quiet` | `all` |\n| `context_size` | `slim`, `full`, `deep` | `full` |\n\nThe two languages are written as the language's own name — `español`, not `es` —\nand read back forgivingly, because this is a file people type into: the English\nname, the ISO code and the spelling without the accent all work.\n\n`voice` is how much of its own work Sardi says out loud — everything, the save\nreminder alone, or nothing. `context_size` is how many memories a session opens\nwith: twenty, fifty or eighty, for a small context window or for a store that\nmatters more than the budget.\n\nTwo of the five are flags as well, because changing them should not mean\nreconfiguring an agent. Either one on its own is a whole command:\n\n```powershell\nleteo setup --language \"español\"\nleteo setup --context slim\n```\n\n## Cloud\n\nOptional, off by default, and per project. Your machine is the client; the cloud\nnever connects back.\n\n**Turning it on** takes two answers: where the server is, and which projects go\nto it. `config set` writes the first into the data directory — into a file with\nrestricted permissions, because it holds a token — and `enroll` names a project.\nNothing replicates until both are done, and the commands below say so rather\nthan starting quietly:\n\n```powershell\nleteo cloud config set --server https://memory.example.com --token YOUR-TOKEN\nleteo cloud enroll --project leteo\nleteo cloud config show\n```\n\n`config show` reads the configuration back with the token replaced by a presence\nflag, so it is safe to paste.\n\n**Once it is on**, `health` asks the server whether it is there and answering.\n`status` contacts nothing at all: it reports this machine's own view — what is\nenrolled, how many changes are waiting and since when, and whether the last\nattempt failed and with what. `sync` runs one cycle now, and `leteo serve` keeps\nrunning them in the background until interrupted:\n\n```powershell\nleteo cloud health\nleteo cloud status\nleteo cloud sync\nleteo serve\n```\n\nNot to be confused with `leteo cloud serve`, which is the other end — the server\nitself, which you only run if you are hosting one. That side, with its Compose\nstack, managed tokens and project grants, is in\n[`openspec/specs/replication.md`](openspec/specs/replication.md).\n\n## Coming From Engram\n\nLeteo is an independent Rust product derived from the workflow and MIT-licensed\nimplementation of Gentleman Programming's Engram. It is not affiliated with or\nendorsed by that project, and promises no drop-in CLI compatibility.\n\nIt reads an Engram database directly, so moving across is a copy. The first\nreports what it would adopt and writes nothing; the second carries it out, and\nrefuses a second time rather than importing everything twice:\n\n```powershell\nleteo import --from-engram --dry-run\nleteo import --from-engram\n```\n\nIt defaults to `~/.engram/engram.db`; pass `--source` for another path. The copy\nfolds in the write-ahead log, so a running Engram's most recent memories come\nacross and its own file is never written to.\n\n## Build\n\nLeteo requires Rust 1.97 or newer.\n\n```powershell\ncargo fmt --all\ncargo test\ncargo clippy --all-targets -- -D warnings\ncargo build --release\n```\n\nCI runs the tests. The formatting and the lints are on you before you commit,\nwhich is why they are listed here and in [`AGENTS.md`](AGENTS.md) rather than\nonly in a workflow.\n\nThe cloud tests need a real PostgreSQL and are skipped without one. Point\n`TEST_DATABASE_URL` at a throwaway database and run `cargo test -- --ignored`;\nthey create and drop their own schema, and are not written to share one.\n\nContributors — human or agent — should read [`AGENTS.md`](AGENTS.md) first.\n\n## Documentation\n\nThis file is the user-facing guide. What the system *guarantees*, and why, is in\n[`openspec/`](openspec/) — one document per capability, cross-linked:\n\n| Document | Covers |\n| -------- | ------ |\n| [`project.md`](openspec/project.md) | what Leteo is, the crate layout, the system-wide invariants |\n| [`specs/memory-model.md`](openspec/specs/memory-model.md) | what a memory is, its types, review windows, normalisation |\n| [`specs/search.md`](openspec/specs/search.md) | matching, ranking, the three stages, the narrowings |\n| [`specs/store-and-schema.md`](openspec/specs/store-and-schema.md) | the database, migrations, `doctor` and its repairs |\n| [`specs/mcp-tools.md`](openspec/specs/mcp-tools.md) | the MCP surface and the shape of its replies |\n| [`specs/hooks.md`](openspec/specs/hooks.md) | the five lifecycle events and their time budgets |\n| [`specs/cli.md`](openspec/specs/cli.md) | the command line and what its answers explain |\n| [`specs/replication.md`](openspec/specs/replication.md) | the optional PostgreSQL peer |\n\nLonger write-ups of individual measurements live in [`docs/`](docs/). The first\nis [*there was nothing worth tuning*](docs/nothing-worth-tuning.md): the third\nsearch stage answers questions belonging to another project 90.2% of the time,\nwhich is *more* often than it answers its own, and four rules swept across their\nwhole range say that is not a threshold anybody can fix.\n\nThe second is [*does memory save tokens?*](docs/does-memory-save-tokens.md),\nwhich asks the question this project was launched with and answers it against\nitself: on questions the repository already answers the saving is inside the\nnoise, on questions it cannot answer the agent without memory spends more and\nstill gets it wrong three times out of three, and six of eleven sampled memories\nfrom the opening block turn out to be recoverable from the repository anyway —\ncode, specs, tests and the git history together.\n\n## Environment\n\n**None of these has to be set.** This is an inventory of every variable the\nbinary reads — a test fails the build when the binary honours one this table\nleaves out — and not a list of things to configure. `leteo setup` writes what an\ninstallation needs into each agent's own configuration file, and the choices you\nmake in the interface are kept in [`settings.json`](#settings). Neither of them\nsets a variable in your environment.\n\nAll but the last are a command-line flag as well, and the flag wins: the\nvariable is read only when the command line does not answer the same question.\n\n| Variable | Flag | Purpose |\n| --- | --- | --- |\n| `LETEO_DATA_DIR` | `--data-dir` | Local data directory; defaults to `~/.leteo` |\n| `LETEO_DATABASE` | `--database` | Explicit local SQLite path |\n| `LETEO_TOOLS` | `mcp --tools` | `agent`, `admin`, `all`, or single tool names. Every tool when nothing names any |\n| `LETEO_PROJECT` | `mcp --project` | Project the MCP server trusts for the whole process; without it, the working directory decides |\n| `LETEO_AGENT_CLI` | `conflicts scan --semantic` | Agent CLI that judges conflict candidates: `claude` or `opencode` |\n| `LETEO_SYSTEM_LANGUAGE` | — | Language this machine works in, when `LANG` does not say. Read once, to offer it in `leteo setup` |\n\nTwo are worth a sentence more, because they are where the flag winning bites:\n\n- `LETEO_TOOLS` is already answered for every agent Leteo sets up: the MCP entry\n  it writes runs `leteo mcp --tools=agent`. Exporting the variable afterwards\n  changes nothing for that agent — edit the profile in its configuration file,\n  or run the setup again.\n- `LETEO_DATA_DIR` is the one with a real reason to be exported. The MCP server\n  is started by the agent rather than by you, so a database somewhere other than\n  `~/.leteo` has to reach it either through that agent's environment or as a\n  `--data-dir` in the command its configuration runs.\n\n### Cloud, on your machine\n\n`leteo cloud config set` persists the server and the token in the data directory\nand is how this is configured. These two are read only where that file leaves\nthe field empty, so a setup that predates it keeps working unchanged.\n\n| Variable | Purpose |\n| --- | --- |\n| `LETEO_CLOUD_SERVER` | Cloud base URL for `cloud health`, `cloud sync` and the client config |\n| `LETEO_CLOUD_TOKEN` | Sync bearer token, at least 32 bytes |\n\n`leteo cloud serve` reads `LETEO_CLOUD_TOKEN` too, as its own legacy static\ntoken — on a machine that is both client and server, one name means two things.\n\n### Cloud, on the server\n\nThese belong to whoever runs `leteo cloud serve`, and they are set where that\nservice is defined — see\n[`docker/docker-compose.yml`](docker/docker-compose.yml). There is no wizard for\nthem on purpose: they are deployment secrets rather than preferences, and none\nof this applies to a normal installation.\n\n| Variable | Purpose |\n| --- | --- |\n| `LETEO_DATABASE_URL` | PostgreSQL URL for cloud serve |\n| `LETEO_DASHBOARD_SECRET` | Dashboard signing secret, at least 32 bytes |\n| `LETEO_CLOUD_TOKEN_PEPPER` | Managed-token HMAC pepper, at least 32 bytes |\n| `LETEO_CLOUD_ADMIN` | Optional legacy admin bearer token, at least 32 bytes |\n| `LETEO_CLOUD_ALLOWED_PROJECTS` | Required allowlist for legacy cloud tokens |\n| `LETEO_CLOUD_HOST` | Cloud bind host; defaults to `127.0.0.1` |\n| `LETEO_CLOUD_PORT` | Cloud port; defaults to `8080` |\n| `LETEO_CLOUD_MAX_POOL` | PostgreSQL connection-pool limit |\n| `LETEO_CLOUD_MAX_PUSH_BYTES` | Maximum cloud push body size |\n\n## License And Attribution\n\nLeteo is distributed under the MIT License. See [LICENSE](LICENSE) and\n[NOTICE](NOTICE) for upstream attribution and the exact reference revision.\nTagged binary archives also include a generated `THIRD_PARTY_LICENSES.html`\ncovering their Rust dependencies.\n",
  "bytes": 26882,
  "sha": "ed8cdf088c19ede0dede2437cb02e31c294833e66df1682eac0e35415498dcea",
  "repo_slug": "asanabrial/leteo",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_asanabrial_leteo_86ad7b14/readme"
}