Skip to main content
The data commands (record, retrieve, reason and everything else that reads or writes memory) work with any of three credentials: A fourth credential, the MCP login, is stored by anona login too, but only anona mcp uses it. The REST API refuses it, so the data commands never fall back to it.

Which credential wins

The first one found is used:
  1. --api-key on the command line
  2. ANONA_API_KEY in the environment
  3. ANONA_API_KEY in ~/.anona/config.env (or the active profile’s config.env)
  4. the command-line login from anona login
  5. a temporary profile from anona start
An API key, wherever it came from, outranks every stored login. With only the MCP login stored, the data commands exit 4 and tell you to run anona login --cli. --api-key is visible to other users of the machine in the process list, so prefer the variable or the file. A space is required with an API key or the command-line login. Both reach an account that can hold many spaces, so the CLI never picks one for you: pass --space, or set ANONA_SPACE_ID in the environment or in config.env (in that order). Without one the command exits 2 and sends nothing. anona spaces list shows the spaces you can use.

Check what a command will use

anona status reads only the local files. It names the API key the data commands would use and where it came from, the command-line login, and the MCP login, each with the server it is for and when its access token expires, and for a temporary profile when the profile expires. It shows at most a key’s first characters (anona_live_abc1…) and never a token. Because it does not call the server, a revoked credential still shows as found. anona doctor (pip 0.25.0; npm 0.19.0) goes further. It reports which Python and which anona are running, which profile and directory are active, which credential a data command would use and why the others lost, which server it talks to, whether that server answers, and whether the space a data command would use is reachable with that credential, and how many credits are left (the same balance anona usage shows). It never prints a secret, and its health check carries no credential at all.
It exits 0 when everything checks out (it prints All good.), and otherwise with the code a data command would fail with: 4 for a credential problem, 7 for a server it cannot reach, 5 for a space that does not exist, 2 when no space is named at all, 6 when no credits are left (reads are refused at zero too). A balance it cannot read is reported as unknown and does not fail the check. With --json it prints one document: {"anona_version", "python", "profile", "profile_dir", "credential": {"kind", "display", "source", "why", "base_url", ...}, "health": {"ok", "status"}, "space": {"id", "source", "accessible", "error"}, "credits": {"remaining", "limit", "used", "exhausted", "error"}, "ok"}.

With an API key

Create a key in the dashboard under API keys, then:
Or keep both in ~/.anona/config.env:
The CLI reads config.env as data and never runs it, takes only ANONA_API_KEY, ANONA_SPACE_ID and ANONA_BASE_URL from it, and warns if the file is readable by anyone but you (chmod 600 ~/.anona/config.env). The agent skills installer writes this file for you.

Where a key may be sent

A key only goes to Anona’s own servers unless you say otherwise in the same place it came from. The default is https://api.anonalabs.com, and a key from the environment or config.env is sent only to api.anonalabs.com or memory.anonalabs.com. It may go to another server only when that server is named beside it: --base-url beside --api-key on the same command line, ANONA_BASE_URL beside ANONA_API_KEY in the environment, or both in config.env. A --base-url on its own never redirects a key you set elsewhere, so a URL an agent was talked into typing cannot carry it off. Anything else is refused with exit 4 before a request is made:
The server must be https://, except localhost and 127.0.0.1.

With anona login

anona login asks for two approvals, one after the other, each in the browser:
  1. The command line, stored in ~/.anona/cli-credentials.json. The data commands use it.
  2. MCP, stored in ~/.anona/credentials.json. anona mcp uses it.
Each is its own app on the approval screen and under Connected apps, with its own refresh token, so revoking or renewing one never touches the other. anona login --cli asks for only the first, anona login --mcp for only the second. If one approval fails, the other still runs, and anona login exits 0 as long as one succeeded; it says which one did not. If a temporary profile is stored, the first approval claims it. A browser opens on the approval screen. If you have no account, you create one there. Read the client name before you approve it: that screen is the whole security model. Over SSH or in a container the browser cannot open on its own, so anona login prints the URL and you open it yourself; --no-browser skips the attempt. The approval redirects back to a local port on the machine running anona login, so open the URL in a browser on that machine, or forward that port. Renewal is automatic. The access token lasts an hour and the refresh token 30 days, rolling: each refresh issues a new 30 days, so a credential you use does not expire. When a data command finds the access token expired it refreshes first, and if the server refuses the token anyway it refreshes once and retries once. You are asked to approve again only after 30 days of total silence, or after you revoke the app under Connected apps in the dashboard; the command then exits 4 and says to run anona login --cli. A stored login only goes to the server that issued it. A --base-url or ANONA_BASE_URL naming a different one is refused with exit 4, not followed:
Switching servers is always an explicit anona login --base-url. For a self-hosted deployment pass --base-url https://your-host or set ANONA_BASE_URL; the flag works before or after the command and wins over the variable.

Signing out

logout deletes the local file and then asks the server to revoke the credential. The file is deleted even when the server cannot be reached, and the CLI says so; a credential it could not revoke dies after 30 days unused, or revoke it under Connected apps. logout refuses to discard a live, unclaimed temporary profile unless you pass --force, because its memories cannot be recovered. With nothing stored, logout says there was nothing to remove and still exits 0. Neither login nor logout touches config.env.

With a temporary profile

anona start creates a temporary profile: a memory workspace with no account, no email address and nobody owning it. An agent can use it immediately, and a person claims it later with anona login, which keeps everything it holds. It prints the deadline:
  • 72 hours, then deletion, memories included. anona status prints the deadline whenever you ask.
  • One space, default. Omit --space. Creating another is refused with temp_profile_single_space until the profile is claimed.
  • 1,000 credits while unclaimed, rising to the normal allowance on claim.
  • Claiming needs an email that has never been used for Anona (claim_needs_new_account otherwise).
  • One live profile at a time. anona start refuses to create a second.
The full walkthrough is on Temporary profiles.

Named profiles

--profile NAME (or ANONA_PROFILE=NAME) switches the whole credential set to ~/.anona/profiles/NAME/: its own command-line login, its own MCP login and temporary profile, its own config.env, and its own locks. Nothing is shared, so a refresh in one profile can never rotate a token in another. Unset, or default, is ~/.anona itself.
A profile name is letters, digits, - and _, at most 64 characters; anything else exits 2. Use one profile per account or per deployment, for example to keep a staging server’s login apart from production’s.
The anona login credentials are for the CLI, not for your code. The MCP one works only on /mcp; the command-line one works only for the anona commands on this machine. The Python and JavaScript SDKs and raw REST calls need an API key, minted in the dashboard under API keys. A temporary profile’s anona_tmp_ token is the exception: it also authenticates the REST API until it is claimed or expires.