Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,21 @@ ENVIRONMENT=development
# docker buildx imagetools inspect ghcr.io/caura-ai/caura-memclaw-core-storage-api:v1.2.3
# CAURA_VERSION=latest

# -- Database (PostgreSQL + pgvector) ----------------------------------------
# -- Database helpers (PostgreSQL + pgvector) --------------------------------
# POSTGRES_HOST/PORT/USER/PASSWORD/DB are inputs for migration and development
# helpers only. They do not configure either running API service, and the stock
# Compose file hardcodes its bundled database connection. For a manual or
# custom core-storage-api deployment, set DATABASE_URL directly instead, e.g.:
# DATABASE_URL=postgresql+asyncpg://caura:change-me@db.example:5432/caura
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=caura
# REQUIRED: change in production
POSTGRES_PASSWORD=changeme
POSTGRES_DB=caura
# Require TLS on connections to Postgres. Set true in production unless the
# database is reached over a private network you already trust. Encrypts and
# refuses a server that will not upgrade; it does not verify the server
# Require TLS on core-storage-api connections to Postgres. Set true in
# production unless the database is reached over a private network you already
# trust. It encrypts the connection and refuses a server that will not upgrade;
# it does not verify the server
# certificate — for that, put ``?ssl=verify-full`` on DATABASE_URL.
POSTGRES_REQUIRE_SSL=false

Expand Down
41 changes: 25 additions & 16 deletions AGENT-INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ You need these on your machine:

- **Git** (to clone the repo)
- **Docker + Docker Compose** (easiest path — handles PostgreSQL + pgvector + Redis)
- OR: **Python 3.11+** and a **PostgreSQL 16 instance with pgvector** (manual path)
- OR: **Python 3.12+** and a **PostgreSQL 16 instance with pgvector** (manual path)

## Option A: Docker (recommended — zero config)

Expand Down Expand Up @@ -50,12 +50,10 @@ pip install -r requirements.txt
# 4. Create .env file
cat > .env << 'EOF'
ENVIRONMENT=development
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=caura
POSTGRES_PASSWORD=changeme
POSTGRES_DB=caura
DATABASE_URL=postgresql+asyncpg://caura:changeme@127.0.0.1:5432/caura
POSTGRES_REQUIRE_SSL=false
CORE_STORAGE_API_URL=http://127.0.0.1:8002
CORE_STORAGE_SHARED_SECRET=dev-only-storage-secret-change-me
IS_STANDALONE=true
EMBEDDING_PROVIDER=fake
ENTITY_EXTRACTION_PROVIDER=fake
Expand All @@ -68,11 +66,15 @@ psql -U postgres -c "CREATE USER caura WITH PASSWORD 'changeme';"
psql -U postgres -c "CREATE DATABASE caura OWNER caura;"
psql -U caura -d caura -c "CREATE EXTENSION IF NOT EXISTS vector;"

# 6. Run migrations
alembic upgrade head
# 6. Start the storage service. It applies Alembic migrations during startup.
PYTHONPATH=.:core-storage-api/src uvicorn core_storage_api.app:app \
--host 127.0.0.1 --port 8002

# 7. Start the server (the FastAPI app lives at core-api/src/core_api/app.py)
PYTHONPATH=core-api/src uvicorn core_api.app:app --host 0.0.0.0 --port 8000
# 7. In a second terminal, activate the same venv, return to the repo root,
# and start core-api. It talks to the storage service on port 8002.
source venv/bin/activate
PYTHONPATH=.:core-api/src uvicorn core_api.app:app \
--host 127.0.0.1 --port 8000

# 8. Verify (in another terminal)
curl http://localhost:8000/api/v1/health
Expand All @@ -95,15 +97,20 @@ Restart the server. REST and MCP calls work without `X-API-Key`. Supplying a
placeholder such as `X-API-Key: standalone` is harmless and can make the same
client configuration easier to reuse with authenticated modes.

**Path 2 — Admin key (multi-tenant, full access).** Set in your `.env`:
**Path 2 — Admin key (multi-tenant, full REST access).** Set in your `.env`:

```env
ADMIN_API_KEY=my-long-random-admin-key
```

Use `my-long-random-admin-key` as `X-API-Key`. You pass `tenant_id` explicitly in request bodies / query params.
Use `my-long-random-admin-key` as `X-API-Key`. You pass `tenant_id`
explicitly in request bodies / query params. Admin/system keys are
intentionally rejected by MCP; use standalone mode, a tenant-scoped key, or
Path 3 for MCP.

**Path 3 — Gate the API with a shared key.** Set `CAURA_API_KEY` in your `.env`. Clients send that key via `X-API-Key` plus `X-Tenant-ID` to pick a tenant. Use this when the OSS API is network-exposed.
**Path 3 — Gate the API with a shared key.** Set `CAURA_API_KEY` in your
`.env`. REST and MCP clients send that key via `X-API-Key` plus `X-Tenant-ID`
to pick a tenant. Use this when the OSS API is network-exposed.

> **Note:** There is no `/ui/pricing.html`, `/api/register`, or `scripts/create_key.py` in OSS. Those are enterprise-plane features. For self-install, use Path 1.

Expand All @@ -124,7 +131,9 @@ Add this to your MCP client configuration (Claude Code, Claude Desktop, Cursor,
}
```

Replace `standalone` with your admin key (Path 2) or the shared gate key (Path 3) as appropriate.
The block above is for Path 1. For Path 3, replace `standalone` with the shared
gate key and add an `X-Tenant-ID` header. Do not use the Path 2 admin key with
MCP; the MCP endpoint rejects admin/system credentials.

> **Claude Code** doesn't read MCP servers from `settings.json` — register with `claude mcp add` instead (the block above maps to a project-root `.mcp.json`). Use `-s user` so the server is available in every directory, not just the one you ran the command in:
> ```bash
Expand Down Expand Up @@ -259,14 +268,14 @@ goes through `caura_doc` on the `skills` collection (`op=write` to share,
The default `fake` providers skip LLM enrichment — memories are stored but not auto-classified. To enable full enrichment (type, weight, title, summary, tags, PII detection, entity extraction, contradiction detection), add an OpenAI key to your `.env`:

```bash
# Add to .env (or .env.dev for Docker)
# Add to .env (or env.dev for Docker)
EMBEDDING_PROVIDER=openai
ENTITY_EXTRACTION_PROVIDER=openai
USE_LLM_FOR_MEMORY_CREATION=true
OPENAI_API_KEY=sk-...
```

Then restart the server (`docker compose restart app` or re-run uvicorn).
Then restart the server (`docker compose restart core-api` or re-run uvicorn).

## What You Now Have

Expand Down
2 changes: 1 addition & 1 deletion clients/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ with **422** and names it:
try:
mc.write("a memory", tags=["alpha"]) # `tags` is not a write field
except CauraAPIError as exc:
exc.payload["error"]["details"]["unknown_fields"] # ["tags"]
exc.details["unknown_fields"] # ["tags"]
```

This used to return `201` with the field silently discarded, so an integration
Expand Down
4 changes: 2 additions & 2 deletions docs/plugin-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ For operators with many nodes (and SSH or OpenClaw-agent reach to all of them),
Identifying which nodes need re-install:

```bash
curl -s "https://your-caura-server/api/v1/fleet/stats?tenant_id=$TENANT_ID&fleet_id=$FLEET_ID" \
curl -s "https://your-caura-server/api/v1/fleet/nodes?tenant_id=$TENANT_ID&fleet_id=$FLEET_ID" \
-H "Authorization: Bearer $JWT" \
| jq '.nodes[]
| jq '.[]
| select((.plugin_version // "0") | split(".") | map(tonumber? // 0) | . < [2,6,0])
| {node_name, plugin_version, last_heartbeat}'
```
Expand Down
14 changes: 9 additions & 5 deletions plugin/skills/caura/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ You auto-register at **trust 1** on your first write.
| 3 | admin | all | all, incl. deletes |

Operations that escalate the required level:
- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2
- `caura_list` / `caura_stats` for another fleet, or either tool with
`scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1
- `caura_insights` with `scope="fleet"` or `"all"` → trust 2
- reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1)
- `caura_manage op=delete` → trust 3

Expand All @@ -207,9 +209,11 @@ than silently retrying at a narrower scope.

**Visibility (on write)** decides who can see a memory: `scope_agent` (private)
· `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant).
**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet`
(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on
read unless you need cross-agent context. *Naming caveat:* writes take
**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For
`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or
`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`.
Prefer `scope_team` on write and `scope=agent` on read unless you need
cross-agent context. *Naming caveat:* writes take
`visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes,
similar spelling.

Expand Down Expand Up @@ -386,7 +390,7 @@ for, and the behaviors that aren't visible in a parameter list.
- **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain.
- **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right.
- **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted` (also in `TOOLS.md`).
- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant.
- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5).
- **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword).
- **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope.
- **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check.
Expand Down
14 changes: 9 additions & 5 deletions plugin/skills/memclaw/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ You auto-register at **trust 1** on your first write.
| 3 | admin | all | all, incl. deletes |

Operations that escalate the required level:
- browsing / reflecting with `scope="fleet"` or `"all"` → trust 2
- `caura_list` / `caura_stats` for another fleet, or either tool with
`scope="all"` → trust 2; `scope="fleet"` for your own fleet stays at trust 1
- `caura_insights` with `scope="fleet"` or `"all"` → trust 2
- reporting outcomes (`caura_evolve`) at `scope="fleet"` / `"all"` → trust 2 (default `scope="agent"` needs only trust 1)
- `caura_manage op=delete` → trust 3

Expand All @@ -207,9 +209,11 @@ than silently retrying at a narrower scope.

**Visibility (on write)** decides who can see a memory: `scope_agent` (private)
· `scope_team` *(default — your fleet)* · `scope_org` (all fleets in tenant).
**Scope (on read / `_list` / `_insights`):** `agent` *(default)* · `fleet`
(trust 2) · `all` (trust 2). Prefer `scope_team` on write and `scope=agent` on
read unless you need cross-agent context. *Naming caveat:* writes take
**Scope (on read):** `agent` *(default)* · `fleet` · `all`. For
`caura_list` / `caura_stats`, your own fleet needs trust 1; another fleet or
`all` needs trust 2. `caura_insights` requires trust 2 for `fleet` or `all`.
Prefer `scope_team` on write and `scope=agent` on read unless you need
cross-agent context. *Naming caveat:* writes take
`visibility=scope_*`; reads/list/keystone filters take `scope=*` — two axes,
similar spelling.

Expand Down Expand Up @@ -386,7 +390,7 @@ for, and the behaviors that aren't visible in a parameter list.
- **`caura_recall`** excludes superseded memories (`status` ∈ {outdated, conflicted}) by default — pass `status` explicitly to walk the chain.
- **`caura_write`** can't write `insight` / `outcome` / `rule` types — those are server-generated (via `caura_insights` / `caura_evolve`). `write_mode`: `fast` skips embedding → keyword-only recall afterwards; `strong` forces full LLM enrichment; `auto` is usually right.
- **`caura_manage op=transition`** targets: `active · pending · confirmed · cancelled · outdated · conflicted · archived · deleted` (also in `TOOLS.md`).
- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` for the single best match across the tenant.
- **`caura_doc`** — `where` is scalar exact-match only (no array descent). A doc is invisible to `op=search` unless it has a `data["summary"]` (the only embedded field). Scope the search to a collection when you know it; omit `collection` to return up to `top_k` matches across the tenant (default 5).
- **`caura_tune`** persists and reshapes every later recall — change one or two knobs at a time; call with no arguments to read your current profile (`fts_weight` 0 = pure semantic, 1 = pure keyword).
- **`caura_insights`** saves findings as `insight` memories; run it at boundaries, not every turn. `focus="divergence"` needs a non-agent scope.
- **`caura_stats`** is read-only — use it as a readiness/health probe, never a write-then-delete check.
Expand Down
Loading
Loading