Multi-project on one shared backend — project scoping¶
mokata's shared backends (a team Postgres DSN for memory, semantic vectors, portable sessions, and the team audit log) are owned + namespaced so they never collide with another app's tables. Stage 71a adds the missing layer: they are also scoped per project, so one shared database can safely host many projects with no cross-project bleed.
Local backends were always clean
Local SQLite (.mokata/…) and the committed vault (.mokata/vault/) are per-repo already. This
page is only about the shared Postgres backends.
The project key¶
Every project has a stable, deterministic project key:
- Configured:
settings.project.idif you set one (mokata config set settings.project.id <id>). - Otherwise derived: the git remote URL — normalized so an
sshclone and anhttpsclone of the same repo agree — else the repo's path, hashed to a short token (p_…).
It is stable across sessions (same repo → same key) and machine-path-free. Two clones of the same repo on different machines resolve to the same key (via the shared remote), so they share one project's data; two unrelated repos never do.
Set an explicit, human-friendly key when a team wants everyone to agree regardless of clone URL:
How every shared row is scoped¶
Each shared table carries a project column; every write, read, list, and delete filters by the
current project key. So a recall, a session list, or an audit --team returns only your
project's rows even when several projects share one DSN. A session tag like auth no longer
collides across projects — each project keeps its own bundle.
Reviewing memory / sessions / audit¶
Review defaults to the current project. Three flags open it up:
| Flag | Effect |
|---|---|
| (none) | the current project only (the default) |
--all |
span every project on the shared backend |
--project <id> |
a specific project |
--list-projects |
print the projects present on the shared backend, then exit |
mokata memory --list-projects # which projects are on this shared DB?
mokata memory --project acme-web # review another project's brain
mokata audit --team --all # who-did-what across every project
mokata session list --all # every project's portable sessions
Outside a project (a bare shell, no .mokata/) pointed at a shared DSN, mokata never silently
dumps every project. It asks you to choose a scope — --all, --project <id>, or
--list-projects to see what's there first.
One DSN per project vs one shared DB¶
Both work:
- One shared DB (recommended now): point every project at the same DSN; project scoping keeps them isolated. Simplest to operate — one database for the whole team.
- One DSN per project: give each project its own database/schema. Also fine (the scoping is simply a no-op when only one project is present). This was the interim operating model before Stage 71a.
Migration — pre-existing shared tables¶
Tables created before Stage 71a gain the project column when the shared schema is provisioned or
upgraded — an idempotent ADD COLUMN IF NOT EXISTS run by mokata team init, which owns all
DDL. (Runtime connections run no DDL at all; they only verify the schema, so a least-privilege
DML-only role is enough — see team setup.
If the column isn't there yet, re-run mokata team init.)
Old rows have no project key and read back as a legacy bucket:
- Scoped reads (the default) do not show legacy rows — no crash, no surprise.
--allsurfaces them, and--list-projectsshows thelegacybucket, so nothing is hidden silently.
To fold legacy rows into a real project, do a one-time backfill against your database, e.g.:
UPDATE mokata_memory SET project = 'acme-web' WHERE project IS NULL;
UPDATE mokata_memory_vectors SET project = 'acme-web' WHERE project IS NULL;
UPDATE mokata_session_bundle SET project = 'acme-web' WHERE project IS NULL;
(Use the key mokata config get settings.project.id reports, or your chosen id.) After the backfill
those rows scope to that project like any other. Everything stays human-gated + secret-scanned
on write, and the DSN secret is never stored — only the env-var name.