A Postgres-backed conversation_store plugin for Animus.
It is the durable, multi-user chat-history backend for the LaunchApp portal,
adding per-user ownership and sharing on top of the kernel's chat surface.
The conversation_store role is optional: with no plugin installed the
kernel falls back to its in-tree filesystem store, so chat works with zero
plugins. Installing this plugin routes the chat data ops to Postgres instead.
Implements the conversation/* method family from animus-plugin-protocol's
conversation_store module:
| Method | Purpose |
|---|---|
conversation/create |
Create a fresh conversation, return its meta. |
conversation/load_meta |
Load one conversation's meta (or null). |
conversation/save_meta |
Persist updated meta (continuity pointer, title). |
conversation/append_message |
Append one turn at a client-authoritative seq. |
conversation/load_messages |
Read the full ordered turn history. |
conversation/list |
List summaries newest-first (owner-scoped). |
conversation/delete |
Permanently remove a conversation (idempotent). |
health/check |
Postgres ping. |
Every conversation carries owner (the portal's authenticated user id;
NULL = unowned) and visibility (private | shared). Requests carry an
optional as_user:
- A
privaterow owned byXis visible/mutable only toas_user = X. - A
sharedrow is visible to everyone. - An unowned (
owner = NULL) row is visible to everyone. list as_user = XreturnsX's own conversations PLUS allsharedones.as_user = NULL(unscoped/admin) sees everything.
The backend scopes every query itself, so it is safe independent of the kernel client.
seq is client-authoritative: the backend persists messages at the seq it is
given and never renumbers. Concurrent appends to one conversation are serialized
with a transaction-scoped Postgres advisory lock, and a colliding seq is
rejected via the UNIQUE (repo_scope, conversation_id, seq) constraint so a
racing turn fails loudly instead of corrupting the log.
Two tables, both scoped by repo_scope so multiple repos sharing one database
do not collide. Created automatically on startup unless
ANIMUS_POSTGRES_AUTO_MIGRATE is set to a falsey value.
CREATE TABLE chat_conversation (
repo_scope TEXT, id TEXT, tool TEXT, model TEXT, session_id TEXT,
title TEXT, created_at TEXT, updated_at TEXT,
message_count BIGINT, owner TEXT, visibility TEXT,
PRIMARY KEY (repo_scope, id)
);
CREATE TABLE chat_message (
repo_scope TEXT, conversation_id TEXT, seq BIGINT,
role TEXT, content TEXT, recorded_at TEXT, tool TEXT, model TEXT,
usage JSONB, cost_usd DOUBLE PRECISION, blocks JSONB,
PRIMARY KEY (repo_scope, conversation_id, seq),
FOREIGN KEY (repo_scope, conversation_id)
REFERENCES chat_conversation(repo_scope, id) ON DELETE CASCADE
);| Env var | Default | Purpose |
|---|---|---|
DATABASE_URL |
— | Postgres connection URL. |
ANIMUS_POSTGRES_URL |
— | Fallback URL when DATABASE_URL unset. |
ANIMUS_POSTGRES_AUTO_MIGRATE |
on | Disable schema bootstrap with a falsey. |
ANIMUS_CHAT_POSTGRES_MAX_CONNECTIONS |
3 | sqlx pool size (kept small by design). |
cargo build --release
./target/release/animus-chat-postgres --manifestElastic-2.0