Khala
Khala is a self-hosted, shared long-term memory for the AI agents you and your team use. Claude, Codex, ChatGPT, local models and bots all read and write the same memory through one remote MCP server, and every agent only sees what its person is allowed to see.
Memories are plain Markdown files in a Git repository. Git is the source of truth and the history; a small SQLite database holds accounts, grants and agent tokens. A web admin lets people review what agents wrote, undo it, and decide who can see what.
The name comes from the Khala of StarCraft's protoss, the sacred link that joins every mind.
What it does
- One memory for every agent. Any MCP client that supports remote servers and OAuth can connect. Clients without OAuth get a long-lived bearer token from the web admin.
- Scopes and grants. Every record belongs to a scope (
personal,work, a project). Scopes are owned by a person and shared with others as viewer, editor or maintainer. What you cannot read does not exist for you: search, history and error messages never reveal it. - Agents with ceilings. Each connected client is an agent of one person. Its access is the person's access narrowed by the agent's own ceiling, and it can be revoked or have its changes undone in one place.
- Notes first, records after review. Agents leave notes in an inbox at the end of a task. Any agent can later consolidate notes into records; the result becomes a proposal a person approves (or a scope can opt into automatic approval). Contradictions become conflicts for a person to settle.
- Strong sign-in without passwords. Passkeys first, TOTP and recovery codes as backup, email codes to get started. OAuth consent is bound to the browser that signed in.
- Git all the way down. Every change is a commit with the agent and its reason in the trailers. A local clone of the repository works as a mirror; a pre-receive hook refuses secrets and oversized records.
Try it locally
Requires Python 3.12+ and Git.
pipx install khala
khala init --admin you@example.com
khala serve
Or from a clone: python3 -m venv .venv && .venv/bin/pip install -e ., then the same commands from .venv/bin.
khala init creates ./khala-data (the record repository, the state database and the key that encrypts TOTP
secrets), makes you the admin and writes the settings to ./khala.env, which khala serve reads. Open
http://localhost:8100/app, sign in as you@example.com, and read the sign-in code from the server log (a local
instance prints mail instead of sending it). Then add a passkey on the Security page and connect an agent to
http://localhost:8100/mcp.
Run it with Docker
On a machine with Docker, whose domain's DNS points at it and with ports 80 and 443 open:
git clone https://github.com/shuiandy/khala && cd khala
cp .env.example .env # set KHALA_DOMAIN, KHALA_OWNERS and the SMTP settings
docker compose up -d
compose.yaml runs Khala behind Caddy, which gets and renews the TLS certificate. Everything the server keeps
(the record repository, the state database and the key that encrypts TOTP secrets) is in the khala-data volume;
back it up, or use the backup scripts below. Open https://<your domain>/app and sign in as the owner.
Run it on a server
The scripts in deploy/ install Khala on a Linux host with systemd, in this layout (every name can be changed
in deploy/local.env):
| Path | Contents |
|---|---|
/srv/khala/app |
the code and its virtualenv (.venv) |
/srv/khala/vault.git |
the record repository (bare) |
/srv/khala/state |
the state database, its pre-deploy copies and backup bookkeeping |
/etc/khala/server.env |
KHALA_* and SMTP_* settings, see below |
/etc/khala/secret.key |
the key that encrypts TOTP secrets (created by deploy.sh on a new server) |
- Install Python 3.12 or later, Git and OpenSSL on the server.
- Write
/etc/khala/server.envwith at leastKHALA_ISSUER,KHALA_OWNERSand the SMTP settings. - Put a TLS reverse proxy in front of port 8100;
deploy/Caddyfile.exampleis a complete Caddy config. - On your own machine, copy
deploy/local.env.exampletodeploy/local.env, fill it in, and runsh deploy/deploy.sh. On a new server it creates thekhalauser, the directories, the virtualenv, an empty record repository and the key. Every time, it installs the package, runs the test suite on the server, installs the systemd units and the pre-receive hook, restarts the service and checks the public endpoints.
Optional backups: deploy/backup.env.example (records to a private Git remote every 15 minutes) and
deploy/state-backup.env.example (a daily age-encrypted snapshot of the state database). Restoring is
described in deploy/RESTORE.md.
Configuration
Settings are environment variables. Names from before the rename (MEMORY_*) are still read, and the server
logs a warning listing them.
| Setting | Default | Meaning |
|---|---|---|
KHALA_ISSUER |
required | Public URL of the server, for example https://memory.example.com. OAuth, passkeys and cookies are bound to it. |
KHALA_ALLOWED_HOSTS |
host of KHALA_ISSUER |
Comma-separated Host values the MCP endpoint accepts. |
KHALA_TRUSTED_PROXIES |
127.0.0.1,::1 |
Peers whose X-Forwarded-For and X-Forwarded-Proto are believed (your reverse proxy), as comma-separated addresses or networks; * for any, empty for none. The client address feeds the audit log and sign-in rate limits. |
KHALA_REPO |
/var/lib/khala/vault.git |
The bare record repository (created on first start). |
KHALA_BRANCH |
main |
The branch that holds the records, and the only ref a mirror may push. Notes and proposals live under refs/khala/*, which ordinary clones do not fetch. |
KHALA_DB |
/var/lib/khala/state/khala.db |
The state database. |
KHALA_STATE_DIR |
directory of KHALA_DB |
Where backup status files are read from. |
KHALA_OWNERS |
none | Emails that become admins on startup. An email that belongs to no account gets an admin account of its own; to make two emails one person, add one as an alias with khala alias. |
KHALA_TIMEZONE |
UTC |
Time zone for "today" (verified and proposed dates, review due) and for times shown on the web, for example America/Toronto. The server's own zone is not used. |
KHALA_OWNER_NAME |
Owner |
Display name for a newly created admin. |
KHALA_MAILER |
smtp |
smtp, or log to print mail to the log (development only). |
KHALA_SECRET_KEY_FILE |
/var/lib/khala/secret.key |
Key that encrypts TOTP secrets, created on a new instance's first start. Keep it out of backups of the database. If the database has accounts and the key is missing, the server refuses to start rather than make a new one. |
KHALA_SECRET_KEY |
none | The key itself, instead of the file. |
KHALA_GIT_PUSHER |
first admin | Account that pushes to the repository over Git (a local mirror). |
KHALA_PUSH_ENFORCE |
off | 1 makes the pre-receive hook refuse pushes that break scope rules; otherwise it only warns. Secrets and oversized records are always refused. |
KHALA_REQUIRE_STRONG_FACTOR |
off | 1 requires every account to have a passkey or an authenticator app: until it adds one, an account signed in with an email code only reaches its security settings and cannot connect agents. Tokens issued before the switch keep working. |
KHALA_ADOPT_EXISTING |
off | 1 lets an empty state database take over a repository that already has records, giving every scope to the first admin. |
KHALA_INSTANCE_NAME |
Khala |
Name shown in page titles, sign-in emails, passkey prompts and authenticator apps. |
KHALA_WRITES_PER_HOUR, KHALA_WRITES_PER_DAY |
60, 300 |
Writes one agent may make (records, proposals, marking outdated). |
KHALA_NOTES_PER_HOUR |
120 |
Inbox notes one agent may leave in an hour. |
KHALA_INBOX_LEASE_MINUTES |
30 |
How long an agent's claim on inbox notes lasts. |
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_FROM |
none | Outgoing mail for sign-in codes. Without a user name the server does not sign in (a local relay). |
SMTP_SECURITY |
ssl |
ssl (implicit TLS, port 465 by default), starttls (port 587) or none (a trusted local relay, port 25). Certificates are always verified. |
Connecting agents
The MCP endpoint is https://your-host/mcp. The quickest way to connect a particular client is on the web admin
at Agents → Connect agent, or from the command line on the computer where the client runs:
khala connect --list
khala connect claude-code --url https://your-host
Both come from one catalog, khala/clients.yaml, which knows install links, one-line
commands and config files for Claude, Claude Code, ChatGPT, Codex, Cursor, VS Code, Gemini CLI, Zed, goose,
Hermes, opencode, Cline, Kiro, Devin Desktop, LM Studio, Junie, Continue and JetBrains AI Assistant, each with
its sources. A new client is a new entry there; tests/test_catalog.py checks it.
How clients sign in:
- OAuth. Clients that publish a client ID metadata document (Claude, Claude Code, ChatGPT, Codex, VS Code, Zed, goose, Hermes) are recognised by it; others register themselves. Either way the person signs in and picks, on a consent page, which scopes the agent may reach.
- A token. For clients that cannot sign in, the wizard or
khala connect --tokencreates one. The command line gets it through device authorization (you approve a short code on the web), so it never goes through the clipboard. - stdio.
khala bridge https://your-host/mcpconnects a client that can only start local servers, withKHALA_TOKENin its environment.khala serve --stdioserves one person on this computer from a local data directory, with no server and no sign-in.
The server's MCP instructions tell agents how to use the memory: look up the relevant scope's index at the start
of a task, read only the few records that matter, and leave a note at the end. Clients that show MCP prompts also
get these routines as commands (recall, remember, tidy_inbox), and clients that attach resources can read
khala://guide, a scope's index and single records.
Records
A record is a Markdown file at the repository root with YAML-style frontmatter:
---
name: data-audit
description: one line saying what this is and when it matters
metadata:
type: project
scope: work
verified: 2026-10-01
review_after: 90d
source: claude-code
status: active
---
The content, in Markdown.
type is one of user, feedback, project, reference, env or issue; by convention the lowercase file
name starts with it (project_data_audit.md). Records past review_after are flagged as due for review. The full
format, and the canonical form the server holds writes to, is in docs/record-format.md.
Command line
khala init sets up an instance and khala serve runs it. Day-to-day administration happens on the web admin;
the other commands are the fallback on the server, run as the service user: khala users, khala add,
khala alias, khala grant, khala revoke, khala disable, khala enable, khala scopes, khala auto-load,
khala agents, khala revoke-agent and khala reset-auth. Every command reads its settings from the
environment, from --env FILE given first, or from ./khala.env. Run khala help for the full list.
Limits
- File names and scope ids share one namespace across the whole instance.
- The server commits under one Git identity; the real author is recorded in commit trailers.
- Admins can see all data.
- One machine: workers share a SQLite file and a Git repository on local disk. Several worker processes
(
--workers N) are fine, since every single-use check is one database statement and Git writes are compare-and-swap; a network file system is not. - Search is a linear scan, fine for thousands of records.
Contributing and security
See CONTRIBUTING.md. Please report vulnerabilities privately as described in SECURITY.md.
License
Metadata
Release files for khala 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| khala-0.3.0.tar.gz | 437.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| khala-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 835.3 kB
Release files / khala-0.3.0.tar.gz
| Download URL | khala-0.3.0.tar.gz |
|---|---|
| Size | 437.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0a3addcb09ec106daf3d082ff10086fdb7070ed6c97eb14cd7ce28b063048005
|
|
BLAKE2b-256 checksum How to use checksums |
a4c75a7ca198e7ce39ddef3d7dc6eb1687e79b4380276f2b304aac2958897e5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency logRelease files / khala-0.3.0-py3-none-any.whl
| Download URL | khala-0.3.0-py3-none-any.whl |
|---|---|
| Size | 397.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90e8ec8a15941688ab3d49896d6d2638f0779fa1a0574d6085f3734984f376b7
|
|
BLAKE2b-256 checksum How to use checksums |
ef84daa82f702d32c5303dfcfe5303b795a90ea35205da49fd0867c4d156c561
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency log